FOR /F issue running from a text file + counting up value - batch-file

Here is something I'm struggeling with:
Title Scanning online computers: && set /a title_count=0
call :next
:next
FOR /F "tokens=1" %%a IN (workstation.txt) do (
title Scanning for online computers: %title_count% / %workstation%
ping -n 1 %%a | find "bytes=" >nul
set /a title_count=title_count+=1
if NOT ERRORLEVEL 1 (
set color=%%a && call includes\what.bat %color_pass% && echo %%a >> logs\reachable.txt
) else (
set color=%%a && call includes\what.bat %color_fail% && echo %%a >> logs\unreachable.txt && echo %%a, offline, % date%, %time% >> logs\offline.txt
)
)
The problem I have here is that the function TITLE is not getting updated while the variable %count_title% is counting up through the script.
set /a title_count+=1
doesn't work either
It's displayed as:
Scanning for online computers 0 / 5
Can sombody tell me what I'm doing wrong here?
Thanks in advance.
Illusion
Hi,
I've tried it the way as suggested:
It finishes the rest of the script when using the last GOTO :EOF
IT doesn't make sense to me, if I remove the last GOTO :eof, only the first row in workstation.txt is getting processed/parsed.
Scanning online computers: && set /a title_count+=1`
call :next
::added as possibly missing
GOTO :EOF
:next
FOR /F "tokens=1" %%a IN (workstation.txt) DO CALL :pingstation %%a
GOTO :EOF
:pingstation
title Scanning for online computers: %title_count% / %workstation%
ping -n 1 %1 | find "bytes=" >nul
set /a title_count+=1
if NOT ERRORLEVEL 1 (
set color=%1 && call includes\what.bat %color_pass% && echo %1 >> logs\reachable.txt
) else (
set color=%1 && call includes\what.bat %color_fail% && echo %1 >> logs\unreachable.txt && echo %1, offline, %date%, %time% >> logs\offline.txt
)
goto :eof
)

Read this: Environment variable expansion occurs when the command is read.
Salient points:
Your variables are expanded right when for command (and its entire body enclosed in parentheses) is parsed.
Use !VARNAME! instead of %VARNAME% to avoid it.
For better portability across OS versions/setups, it's a good idea to stick a setlocal EnableExtensions EnableDelayedExpansion at the beginning of your batch file.
Also, make sure there is a goto (e.g., goto :EOF) after call :next, because the code as posted will run through next one extra time.

You can go with setlocal EnableDelayedExpansion and changing the % syntax to the ! one when addressing vars inside the loop that are initialised within that very loop, just as atzz has suggested.
But there's a different approach. You can simply move the body of the loop to a(nother) subroutine. That way the variables would expand as expected.
Title Scanning online computers: && set /a title_count=0
call :next
::added as possibly missing
GOTO :EOF
:next
FOR /F "tokens=1" %%a IN (workstation.txt) DO CALL :pingstation %%a
GOTO :EOF
:pingstation
title Scanning for online computers: %title_count% / %workstation%
ping -n 1 %1 | find "bytes=" >nul
set /a title_count+=1
if NOT ERRORLEVEL 1 (
set color=%1 && call includes\what.bat %color_pass% && echo %1 >> logs\reachable.txt
) else (
set color=%1 && call includes\what.bat %color_fail% && echo %1 >> logs\unreachable.txt && echo %1, offline, %date%, %time% >> logs\offline.txt
)

Related

Getting goto was unexpected at the time

I am currently making a game that has a persuasion system in it. I had all the code for one of the systems set up, but then I set up 2 more, and it started give me an error that said '(number) was not expected at this time'. when I put in 2 for the second choice, and 3 for the 3rd choice.
The code is like this.
#echo off
SETLOCAL EnableDelayedExpansion
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
set "DEL=%%a"
)
set name=Quantum
cls
color 0a
Echo King Redwood: So 2000?
pause >nul
echo.
call :colorText 09 "1. 2500"
echo.
call :colorText 0e "2. 3000"
echo.
call :colorText 0c "3. 4000"
echo.
echo 4. 2000
echo.
set /p "purs=Enter:"
if /i %purs% == 1 (
goto CheckB )
if /i %purs% == 2 (
goto CheckY )
if /i %purs% == 3 (
goto CheckR )
if /i %purs% == 4 (
goto Convo )
:CheckB
set bleu=%random:~-2,1%
if %bleu% GTR 10 (
goto CheckB )
if %bleu% LSS 0 (
goto CheckB )
set /a num = 3
set /a reward = 2500
goto Res
:CheckY
set Yel=%random:~-2,1%
if %Yel% GTR 10 (
goto CheckY )
if %Yel% LSS 0 (
goto CheckY )
set /a num = 5
set reward = 3000
goto Res
:CheckR
set red=%random:~-2,1%
if %red% GTR 10 (
goto CheckB )
if %red% LSS 0 (
goto CheckB )
set /a num = 7
set /a reward = 4000
goto Res
:Convo
set /a reward = 2000
Echo %name%: I think that is a reasonable price.
Echo King Redwood: Very well.
Echo King Redwood: We will now take you to make sure you are
echo ready.
pause >nul
:Res
if %bleu% GEQ %num% goto Succeed
if NOT %bleu% GEQ %num% goto Fail
:Succeed
Echo %name%: I think that the struggles for such a long trip will be more then that
Echo %name%: How about %reward%?
Echo King Redwod: OK %reward% will work.
pause >nul
goto end
:Fail
Echo %name%: I think that you can give me %reward%.
Echo %name%: You know, for the struggles that there will be along the way.
echo If 2000 isn't good enough for you, I'll just have someone else do it.
pause >nul
:end
exit
:colorText
echo off
<nul set /p ".=%DEL%" > "%~2"
findstr /v /a:%1 /R "^$" "%~2" nul
del "%~2" > nul 2>&1i
First, make sure to close the FOR loop by putting a ) before :CheckB.
For the 'was not expected at this time' error, you're sometimes comparing an empty variable to something. For example, by following CheckY, you set Yel, then proceed to Res and check Bleu, which is empty because it hasn't been set. You're putting nothing next to the binary GEQ operator, and that's why it's complaining.
Tip: to debug, try inserting ECHO statements like this:
:Res
echo bleu=%bleu%,num=%num%
Another problem: when using SET, do not surround the = by spaces. SET /a will work with spaces around =, just because of the nature of /a, but plain SET will not. Well, it will append your variable name with a space and prepend your value with a space, which is not what you want.
Another tip: you can constrain what RANDOM returns through SET /a and the modulus operator, like this.
SET /a red=%random% %% 11
This will set red to a number between 0 and 10, so there is no need for the substrings and goto routines you're using after picking your random number.
Also, consider using EXIT /b to exit the batch file and not the whole CMD environment.

IF/ELSE block running everything

I am making a program to run speed/distance/time calculations. This code works when the surrounding "IF %type%==s(...)" statement is removed, but not when I put it back. It seems to run all the lines? Here's the code:
#ECHO off
COLOR 0f
TITLE Speed Distance Time Calculator
SETLOCAL enabledelayedexpansion
:BEGIN
SET /P type="Calculate speed/distance/time? (S/D/T): "
CLS
IF /I %type%==s (
SET /P distance="Distance: "
CLS
:SDUNITS
SET /P dUnits="Distance units (mile/m/km):"
IF /I "%dUnits%"=="mile" (
SET /a multiply = 0.000621371
) ELSE IF /I "%dUnits%"=="m" (
SET /a multiply = 1
) ELSE IF /I "%dUnits%"=="km" (
SET /a multiply = 0.001
) ELSE (
ECHO "Please type mile/m/km: "
GOTO SDUNITS
)
SET /P time="Time: "
CLS
:STUNITS
SET /P tUnits="Time units (h/m/s):"
IF /I "%tUnits%" == "h" (
SET /a divide = 3600
) ELSE IF /I "%tUnits%" == "m" (
SET /a divide = 60
) ELSE IF /I "%tUnits%" == "s" (
SET /a divide = 1
) ELSE (
ECHO "Please type h/m/s: "
GOTO STUNITS
)
FOR /F %%B IN ('powershell !distance! / !time! * !multiply! / !divide!') DO SET total=%%B
) ELSE (
ECHO "Please type S/D/T"
GOTO BEGIN
)
ECHO !total!
PAUSE
EXIT
Any help would be greatly appreciated.
First, there are a couple of major problems here.
Delayed Expansion issues. As already stated in the comments above, this is a pretty major problem that you'll need to understand better. This alone, I think renders this approach with the giant IF block with nested IF statements impossible in a windows batch program. You'd probably be better off using GOTO (inelegant as it may be):
...
set /p type="Calculate [S]peed, [D]istance, or [T]ime? "
if /i %type%=S (goto calcSpeed)
else if /i %type%=D (goto calcDist)
else if /i %type%=T (goto calcTime)
:calcSpeed
set /p t="Enter the time: "
:EnterTimeUnits
set /p tUnits="Enter the time units - [H]ours, [M]inutes, [S]econds: "
if /i "%tUnits%"=="H" (
set /a divide=3600
) else if /i "%tUnits%"=="M" (
set /a divide=60
) else if /i "%tUnits%"=="S" (
set /a divide=1
) else (
echo Try again.
goto EnterTimeUnits
)
...
Integers only. Also mentioned in the comments above, windows batch variables can only handle integers and strings. No floating point decimal numbers of any kind. You'll have to rearrange your logic so that you can put your multiply value into the powershell command. Something like:
...
if /i "%dUnits%"=="mile" (
for /f %%B IN ('powershell %d%/%t%*0.000621371/%div%') DO SET total=%%B
) else if /i "%dUnits%"=="m" (
for /f %%B IN ('powershell %d%/%t%*1/%div%') DO SET total=%%B
) else if /i "%dUnits%"=="km" (
for /f %%B IN ('powershell %d%/%t%*0.001/%div%') DO SET total=%%B
...
With those out of the way, we can proceed to help with your actual question:
[The] code works when the surrounding IF statement is removed, but not when I put it back. It seems to run all the lines.
I'm not seeing the issue as you describe. However, because of the delayed expansion issues in your nested IF statements, it does some funny looping, asking for the same input twice. I think once you deal with those issues, your other problems will clear up.
Troubleshooting: CLS and #ECHO OFF are great for keeping your console tidy, but they make troubleshooting/debugging your program very difficult. Removing those lines will help you see what's going on.

Invoking counter in batch

I m just trying to invoke a simple counter.To implement that I wrote the below script but the script is only giving me output as "Checking".
#echo off
echo checking
goto :check
:check
for /L %%a IN (1,1,4) do (
echo %%a
if %%a == 4 (
echo a is 4 now
echo congo
goto:eof
) else (
goto :check
)
a few Problems here:
1) you are missing a closing parantheses (very good visible when Code is properly intended)
2) any goto breaks your block (a block is everything between (and )
3) jumping ahead of your for Loop will start it again, resulting in a endless Loop
4) no Need to goto :eof, as for will end of it's own when the Counter reaches 4
5) no need to goto <a label at the very next line>
This results in the following Code:
#echo off
echo checking
for /L %%a IN (1,1,4) do (
echo %%a
if %%a == 4 (
echo a is 4 now
echo congo
)
)

What is wrong with this Windows Batch code?

REM test.bat
#ECHO OFF
IF NOT "%~1" == "" IF NOT "%~2" == "" (
SET /A "X=%~1+%~2"
echo %X%
)
Instead of getting the sum of the two numbers, it always prints the previous value of X:
>test.bat 1 2
ECHO is off
>test.bat 3 4
3
>test.bat 4 4
7
Is that because I'm not using EnableDelayedExpansion?
Edit: Why does it work when I move the echo out of the scope?
#ECHO OFF
IF NOT "%~1" == "" IF NOT "%~2" == "" (
SET /A "X=%~1+%~2"
GOTO why
)
GOTO :EOF
:why
echo %X%
As you pointed the delayed expansion - I suppose you know how to fix it - the only (afaik) reason to want to avoid it's usage is if you want the variables to survive endlocal statement and to use the variable value in brackets context meanwhile .So you can use additional CALL or tunneling:
#ECHO OFF
:: using call
IF NOT "%~1" == "" IF NOT "%~2" == "" (
SET /A "X=%~1+%~2"
call echo %%X%%
)
#ECHO OFF
:: using tunneling
setlocal enableDelayedExpansion
IF NOT "%~1" == "" IF NOT "%~2" == "" (
SET /A "X=%~1+%~2"
rem echo !X!
)
endlocal & set x=%x%
echo %x%
EDIT WAS MADE
The call starts a new sub-context of the command prompts with inherited variables.Additional %s are used to expand the variable in the next level.
Why does it work when I move the echo out of the scope?
#ECHO OFF
IF NOT "%~1" == "" IF NOT "%~2" == "" (
SET /A "X=%~1+%~2"
GOTO why
)
GOTO :EOF
:why
echo %X%
GOTO breaks the brackets context ,but in this case even without GOTO the correct value of the %x% will be printed because is outside of the brackets.

How to check a string does not start with a number in Batch?

How can I check that the first character of a string is a letter and so that it is not a number, or rather a cipher? There are no spaces or special characters in this string.
#ECHO OFF
SETLOCAL
SET /a num=5678
CALL :initnum
SET "num=hello"
CALL :initnum
SET "num=4ello"
CALL :initnum
SET "num=hell0"
CALL :initnum
SET "num=he8lo"
CALL :initnum
SET "num="
CALL :initnum
ECHO(==============
SET /a nam=7654
SET "nem=hello"
SET "nim=4ello"
SET "nom=hell0"
SET "num=he8lo"
SET "nzm="
CALL :initnum2 nam
CALL :initnum2 nem
CALL :initnum2 nim
CALL :initnum2 nom
CALL :initnum2 num
CALL :initnum2 nzm
GOTO :EOF
:initnum
IF NOT DEFINED num ECHO NUM is empty, so it doesn't begin with a numeric&GOTO :EOF
FOR /l %%a IN (0,1,9) DO IF %num:~0,1%==%%a ECHO %num% Begins with numeric&GOTO :EOF
ECHO %num% Does NOT begin with a numeric
GOTO :eof
:initnum2
IF NOT DEFINED %1 ECHO %1 is empty, so it doesn't begin with a numeric&GOTO :EOF
CALL SET "$1=%%%1%%"
FOR /l %%a IN (0,1,9) DO IF %$1:~0,1%==%%a ECHO %1 (%$1%) Begins with numeric&GOTO :EOF
ECHO %1 (%$1%) Does NOT begin with a numeric
GOTO :eof
You should be able to get what you want from this demo.
#echo off
setlocal enableextensions disabledelayedexpansion
set "var=1hello"
for /f "tokens=* delims=0123456789" %%a in ("%var%") do (
if not "%%a"=="%var%" echo var starts with a number
)
If the var contents starts with a number, the token/delim management in the for command will remove it.
edited just to include the usual (included the previous code) and some less used options just in case someone is interested
#echo off
setlocal enableextensions disabledelayedexpansion
set "var=1hello"
echo(%var%
rem Option 1 - Use the for command to tokenize the string
rem A dot is added to handle empty vars
for /f "tokens=* delims=0123456789" %%a in ("%var%.") do (
if not "%%a"=="%var%." (
echo var starts with a number
) else (
echo var does not start with a number
)
)
rem Option 2 - Use set arithmetic and detect errors
rem This will fail if the string starts with + or -
set "%var%_=0"
set /a "test=%var%_" 2>nul
if not errorlevel 1 (
echo var does not start with a number
) else (
echo var starts with a number
)
rem Option 3 - Use substring operations and logic operators
set "test=%var%."
if "%test:~0,1%" GEQ "0" if "%test:~0,1%" LEQ "9" set "test="
if defined test (
echo var does not start with a number
) else (
echo var starts with a number
)
rem Option 4 - Use findstr
rem This is SLOW as findstr needs to be executed
echo(%var%|findstr /b /r /c:"[0-9]" >nul && (
echo var starts with a number
) || (
echo var does not start with a number
)
I think this is the simplest way:
#echo off
setlocal EnableDelayedExpansion
set digits=0123456789
set var=1something
if "!digits:%var:~0,1%=!" neq "%digits%" (
echo First char is digit
) else (
echo First char is not digit
)
The first character of var is tried to be removed from digits string. If such a char was a digit, digits string change; otherwise, digits string remains the same.
#echo off
setlocal
set "the_string=a23something"
for /l %%a in (%the_string% ; 1 ; %the_string%) do set "cl_string=%%~a"
if %the_string:~0,1% neq 0 if "%cl_string%" equ "0" (
echo does not start with number
) else (
echo starts with number
)
endlocal
Another approach is with FINDSTR which eventually will be slower as it is an external for cmd.exe command.
#echo off
set "the_string=something"
echo %the_string%|findstr /b /r "[0-9]" >nul 2>&1 && (
echo starts with number
) || (
echo does not start with number
)
This will work in your situation:
echo %variable%|findstr "^[a-zA-Z]" >nul && echo it starts with an alpha character
Using findstr with regexp :
#echo off
set "$string=2toto"
echo %$string:~0,1%|findstr /i "^-*0*x*[0-9][0-9]*$">nul && echo is NUM || echo Is not NUM
in place of echo is NUM or echo is not NUM you can use a goto to redirect your script the way you want it.
#echo off
set "$string=2toto"
echo %$string:~0,1%|findstr /i "^-*0*x*[0-9][0-9]*$">nul && goto:isnum || goto:isnotnum
:isnum
echo is NUM
exit/b
:isnotnum
echo is not NUM
You have to set the string as a variable; in this way you are able to extract substrings from a main string. Here is an example:
#echo off
set EXAMPLESTRING=12345abcde
set FIRSTCHARACTERSTRING=%EXAMPLESTRING:~0,1%
The result of this short script should be 1 in this case.
Then, you can set a series of conditions to verify whether the first character is a number or not:
if %FIRSTCHARACTERSTRING%==0 goto NUMBER
if %FIRSTCHARACTERSTRING%==1 goto NUMBER
if %FIRSTCHARACTERSTRING%==2 goto NUMBER
if %FIRSTCHARACTERSTRING%==3 goto NUMBER
if %FIRSTCHARACTERSTRING%==4 goto NUMBER
if %FIRSTCHARACTERSTRING%==5 goto NUMBER
if %FIRSTCHARACTERSTRING%==6 goto NUMBER
if %FIRSTCHARACTERSTRING%==7 goto NUMBER
if %FIRSTCHARACTERSTRING%==8 goto NUMBER
if %FIRSTCHARACTERSTRING%==9 goto NUMBER
goto LETTER
:NUMBER
echo The first character is a number!
goto EOF
:LETTER
echo The first character is a letter!
goto EOF
Maybe this is not the most efficient solution but it works fine and it is easier to understand.

Resources