windows command line script variable going wrong somewhere? - windows

Battling with a windows command line script I just can't get to work.
Basically I am trying to launch a program called vnctv.exe with the parameters of HOST ipaddress PORT 5900 PASSWORD x, however I only want to run the program with IP addresses of computers currently online.
I've tried a few different things but I cant get anything to work properly.
It seems when I run cmd and type in the commands individually they all work correctly but when I run it in a .bat file it fails.
I have tried enabledelayedexpansion so I wouldn't need to call but I couldn't get that to work.
The for loop first loops through 30-255 addresses. The next loop pings the IP and finds the loss rate. If it's 0 then I need to add that address to a run parameter for vnctv.exe. I've tried echoing to file then removing the \n characters but can't get that to work either. So the script should add the parameter to a variable but it fails.
here it is
set _megga=vnctv.exe
FOR /L %%A IN (253,1,255) DO (
FOR /F "tokens=2 delims=(%%" %%B
IN ('PING -w 500 -n 1 91.40.20.%%A -w 500 -n 1 ^|find "loss"')
DO ( if %%B EQU 0 call :exec ))
)
:exec
set %%_megga=%_megga% HOST 91.40.20.%%A PORT 5900 PASSWORD x
goto :EOF
echo %%_megga > run.bat
run.bat
and heres the output
Z:\>set _megga=vnctv.exe
Z:\>FOR /L %A IN (253 1 255) DO (FOR /F "tokens=2 delims=(%" %B IN ('PING -w 500
-n 1 91.40.20.%A -w 500 -n 1 |find "loss"') DO (if %B EQU 0 call :exec ) )
Z:\>(FOR /F "tokens=2 delims=(%" %B IN ('PING -w 500 -n 1 91.40.20.253 -w 500 -n
1 |find "loss"') DO (if %B EQU 0 call :exec ) )
Z:\>(if 0 EQU 0 call :exec )
Z:\>set %_megga=vnctv.exe HOST 91.40.20.%A PORT 5900 PASSWORD x
Z:\>goto :EOF
Z:\>(FOR /F "tokens=2 delims=(%" %B IN ('PING -w 500 -n 1 91.40.20.254 -w 500 -n
1 |find "loss"') DO (if %B EQU 0 call :exec ) )
Z:\>(if 0 EQU 0 call :exec )
Z:\>set %_megga=vnctv.exe HOST 91.40.20.%A PORT 5900 PASSWORD x
Z:\>goto :EOF
Z:\>(FOR /F "tokens=2 delims=(%" %B IN ('PING -w 500 -n 1 91.40.20.255 -w 500 -n
1 |find "loss"') DO (if %B EQU 0 call :exec ) )
Z:\>(if 100 EQU 0 call :exec )
Z:\>set
%_megga=vnctv.exe HOST 91.40.20.%A PORT 5900 PASSWORD x
_megga=vnctv.exe
Z:\>set %_megga=vnctv.exe HOST 91.40.20.%A PORT 5900 PASSWORD x
Z:\>goto :EOF
When I set it should read 91.40.20.254 for instance instead of %A and also it's making 2 different variables
Thanks for any Help
Adam
tried this but still getting %1 or 1 instead of the actual value
set _megga=vnctv.exe
FOR /L %%A IN (253,1,255) DO (
FOR /F "tokens=2 delims=(%%" %%B IN
('PING -w 500 -n 1 91.40.20.%%A -w 500 -n 1 ^|find "loss"')
DO ( if %%B EQU 0 call :exec %%A))
:exec
set %%_megga=%%_megga HOST 91.40.20.%%1 PORT 5900 PASSWORD x
goto :EOF

The initial complaint that your cmd processor is throwing out is that you dont seem to end the tokens=2 delims=(% options string. This is because you are using the special % in your delim set. Try putting a double % there:
"tokens=2 delims=(%%"
Here is a different strategy to approach your problem:
setlocal enabledelayedexpansion
set _megga=vnctv.exe
echo echo running %_megga% batch > run.bat
FOR /L %%A IN (253,1,255) DO (
FOR /F "tokens=2 delims=(%%" %%B IN ('PING -w 500 -n 1 91.40.20.%%A -w 500 -n 1 ^|find "loss"') DO (
if %%B EQU 0 set _megga=!_megga! HOST 91.40.20.%%A PORT 5900 PASSWORD x ))
)
echo %_megga% >> run.bat
run.bat
I have taken the gist of your loops, but instead of jumping out to a label, I concatenate the command in the loop and later echo it to the run.bat which can then be run at the end. You will note that I employ the setlocal enabledelayedexpansion and use ! to replace % in the loop.

try escaping %:
"tokens=2 delims=(%%"
Edit: to reflect comment
Also note that that the inner for loop's variables can clash with the the outers. When %G is set, the other tokens after G (%h & %i) may be overwritten. I suggest you change %i in the outer loop to be %a.

Related

Errorlevel always returns 0 in a for loop with ping command

I am trying to create a batch script that will ping a machine by hostname, save ip and domain to a variable and display the state (ON or OFF).
How can i fix this code so it will display the status correctly?
My code always returns ON even if pc is actually OFF.
#echo off
setlocal ENABLEDELAYEDEXPANSION
set hostname=non-existent-hostname
set domain=UNRESOLVED
set ip=UNRESOLVED
for /f "tokens=2,3 delims= " %%b in ('ping -a -4 -n 1 !hostname! ^| find "Pinging"') do (
set domain=%%b
set ip=%%c
)
if errorlevel 1 (
echo !hostname! !ip! [!domain!] is OFF
) else (
echo !hostname! !ip! [!domain!] is ON
)
pause
I have another approach using this kind of ping, because my machine is French.
For example, this batch script pings a list of URLs and displays their status (ON/OFF) with different colors.
The URLs are defined in the "URLS" variable.
The script loops through the URLs, formats them using a StringFormat function, gets the IP address of each URL using the "ping" command, and then displays the status of each URL (ON or OFF) with a color using a PSColor function.
#echo off
Title Multi-Ping hosts with colors
Set URLS="non-existent-hostname","https://www.google.com","Nothingtotest","https://www.yahoo.com","www.reddit.com",^
"http://www.wikipedia.com","www.stackoverflow.com","www.bing.com","NoBodyHere.com"
setlocal ENABLEDELAYEDEXPANSION
for %%a in (%URLS%) do (
Call :StringFormat "%%~a"
set "ip="
for /f "tokens=2 delims=[]" %%b in ('ping -n 1 !URL!') do set "ip=%%b"
ping -n 1 !URL!>nul && set "msg=!URL! - !ip! ON" && Call :PSColor "!msg!" Green \n || set "msg=!URL! - !ip! OFF" && Call :PSColor "!msg!" Red \n
)
pause & Exit
::---------------------------------------------------------------------------------
:StringFormat <URL>
(
echo Function StringReplace(Str^)
echo Str = Replace(Str,"http://",""^)
echo Str = Replace(Str,"https://",""^)
echo StringReplace = str
echo End Function
echo wscript.echo StringReplace("%~1"^)
)>"%tmp%\%~n0.vbs"
for /f "delims=" %%a in ('Cscript /nologo "%tmp%\%~n0.vbs"') do ( set "URL=%%~a" )
If Exist "%tmp%\%~n0.vbs" Del "%tmp%\%~n0.vbs"
exit /b
::---------------------------------------------------------------------------------
:PSColor <String> <Color> <NewLine>
If /I [%3] EQU [\n] (
Powershell Write-Host "`0%~1" -ForegroundColor %2
) Else (
Powershell Write-Host "`0%~1" -ForegroundColor %2 -NoNewLine
)
Exit /B
::---------------------------------------------------------------------------------
I figured it out, thanks everyone for hints.
#echo off
setlocal ENABLEDELAYEDEXPANSION
set hostname=non-existent-hostname
set domain=UNRESOLVED
set ip=UNRESOLVED
for /f "tokens=2,3 delims= " %%b in ('ping -a -4 -n 1 !hostname! ^| find "Pinging"') do (
set domain=%%b
set ip=%%c
)
ping -n 1 %hostname% >nul
if %errorlevel% == 0 (
echo %hostname% %ip% [%domain%] is ON
) else (
echo %hostname% %ip% [%domain%] is OFF
)
pause

Why does Windows batch file processing exit on IF condition because of a syntax error?

I'm running a batch script that checks the status of a server, and if it's up, it goes ahead and reboots another server.
I put a few PAUSE commands in there because I want to see where it's falling over. It seems do the first ping, and then fail on the IF statement with:
The syntax of the command is incorrect.
Please can anyone tell me where I'm going wrong?
#echo off
setlocal enableextensions enabledelayedexpansion
set ipaddr=10.2.17.24
:loop
set state=down
for /f "tokens=5,7" %%a in ('ping -n 1 !ipaddr!') do (
if "x%%a"=="xReceived" if "x%%b"=="x1," set state=up
)
echo.Link is !state!
ping -n 6 10.2.17.24 >nul: 2>nul:
PAUSE
IF "%state%" == "down"
GOTO:loop
else
(
echo.DB server back up
PAUSE
echo.Continuing to web server reboot
PAUSE
set Webipaddr=10.2.17.36
PAUSE
:loop
set Webstate=down
for /f "tokens=5,7" %%a in ('ping -n 1 !Webipaddr!') do (
if "x%%a"=="xReceived" if "x%%b"=="x1," set Webstate=up
)
echo.Link is !Webstate!
ping -n 6 10.2.17.36 >nul: 2>nul:
PAUSE
IF "%Webstate%" == "down"
GOTO:loop
else
echo "Both servers back up."
endlocal
On an if statements, if you want to write commands with newlines, you need to use parenthesis, like this:
IF 1==1 (
FOO
) ELSE (
BAR
)
So We need to:
add parenthesis to first IF (before ELSE)
close the ELSE of the first IF (you seem to be missing a ')')
the last IF must be fixed to this form: IF 1==1 (FOO) ELSE (BAR)
The code, fixed:
#setlocal enableextensions enabledelayedexpansion
#echo off
set ipaddr=10.2.17.24
:loop
set state=down
for /f "tokens=5,7" %%a in ('ping -n 1 !ipaddr!') do (
if "x%%a"=="xReceived" if "x%%b"=="x1," set state=up
)
echo.Link is !state!
ping -n 6 10.2.17.24 >nul: 2>nul:
PAUSE
IF "%state%" == "down" (
GOTO:loop
) else (
echo.DB server back up
PAUSE
echo.Continuing to web server reboot
PAUSE
set Webipaddr=10.2.17.36
PAUSE
:loop
set Webstate=down
for /f "tokens=5,7" %%a in ('ping -n 1 !Webipaddr!') do (
if "x%%a"=="xReceived" if "x%%b"=="x1," set Webstate=up
)
echo.Link is !Webstate!
ping -n 6 10.2.17.36 >nul: 2>nul:
)
PAUSE
IF "%Webstate%" == "down" (GOTO:loop) else (echo "Both servers back up.")
endlocal

Variable not emptied in batch script after set to empty

I'm trying to make a batch script which is supposed to ping a site, log the results, and start a program if the results were negative. This is a modification of original script (not mine), which can be found here. Values of domain, IP, and program variables are for illustrative purposes.
#echo off
cls
set domain=testsite.com
set IP=133.78.17.101
set program=c:\windows\notepad.exe
set output=c:\log.txt
set result=1
:Start
IF [%result%]==[] (
>>%output% echo -----------
start %program%
)
ECHO Pinging %domain%...
FOR /F "delims=" %%G in ('ping -n 1 %domain% ^| find "Reply"') DO SET result=%%G
IF NOT [%result%]==[] (
goto Success
) ELSE (
goto TryAgain
)
:TryAgain
ECHO %domain% unreachable. Trying again...
FOR /F "delims=" %%G in ('ping -n 1 %domain% ^| find "Reply"') DO SET result=%%G
IF NOT [%result%]==[] (
goto Success2
) ELSE (
goto TryIp
)
:TryIp
ECHO %domain% unreachable. Pinging %ip%...
FOR /F "delims=" %%G in ('ping -n 1 %IP% ^| find "Reply"') DO SET result=%%G
IF NOT [%result%]==[] (
goto SuccessDNS
) ELSE (
goto TestInternet
)
:TestInternet
ECHO %ip% unreachable. Testing internet connection.
FOR /F "delims=" %%G in ('ping -n 1 www.google.com ^| find "Reply"') DO SET result=%%G
IF NOT [%result%]==[] (
goto Success3
) ELSE (
goto NetDown
)
:Success
>>%output% ECHO Connected
>>%output% echo %date% %time% %result%
ping -n 3 127.0.0.1 > nul
goto Start
:Success2
>>%output% ECHO Connected with packet loss.
>>%output% echo %date% %time% %result%
set result=
ping -n 3 127.0.0.1 > nul
goto Start
:Success3
>>%output% ECHO Domain %domain% not reachable. Connected via IP.
>>%output% echo %date% %time% %result%
set result=
ping -n 3 127.0.0.1 > nul
goto Start
:SuccessDNS
>>%output% ECHO DNS problem.
>>%output% echo %date% %time% %result%
set result=
ping -n 3 127.0.0.1 > nul
goto Start
:NetDown
>>%output% ECHO No internet connection.
>>%output% echo %date% %time% %result%
set result=
ping -n 3 127.0.0.1 >nul
goto Start
What I'm trying to achieve is this - if anything other than a perfect reply to a ping request is received, the script should start a program. To secure that this happens only then, I've been clearing the result variable every time, other than on an expected ping response.
Echoing the value of result keeps returning 1, even after I've emptied it.
in your line
FOR /F "delims=" %%G in ('ping -4 -n 1 %domain% ^| find "Reply"') DO SET result=%%G
%%G is either not defined (when the word Reply doesn't occur), which doesn't touch your Result variable at all,
or a line like Reply from x.x.x.x : Bytes=32 Time<1ms TTL=128, which definitively isn't empty.
According to the rest of your code, you probably meant ... DO SET "result=" to unset the variable.
Note: searching for "Reply" is
a) language dependent ("Antwort" on German Windows) and
b) not reliable (think of Reply from localhost: destination address unreachable).
Better search for TTL= (even works without a for loop):
ping -n 1 %IP% | find "TTL=" >nul && set "reply=true" || set "reply=false"
echo %reply%
Just an addendum to Stephan's post.
In addition to reasons posted in Stephan's post, the code was not working as I had originally planned it to, because the comparisons were failing as well. If there was a successful ping, the result variable was being set to a string consisting of multiple words, which was breaking the comparison. To avoid this, I had to change every instance of
IF [%result%]==[]
to
IF ["%result%"]==[""]
Thomas Weller's (now deleted) comment was also on the right track - I did need to empty the result variable in :Start:
:Start
IF ["%result%"]==[""] (
>>%output% echo -----------
start %program%
)
SET result=
ECHO Pinging %domain%...
This emptying was needed to annul any previous successes and the original set result=1 (in case of consistent failures).

Batch Script to find out whether a host is up or down

I need to scan several systems in the network within IP range 172.18.x.x to 172.25.x.x and check whether each of them is active or not, and then dump the result in a file.
Following is the script:
#echo off
for /L %%x in (18,1,25) do (
for /L %%y in (1,1,254) do (
for /L %%z in (1,1,254) do (
ping -n 1 172.%x.%y.%z | find "Reply" > NUL
IF NOT ERRORLEVEL 1 (set state=UP) ELSE (set state=DOWN)
echo 172.%%x.%%y.%%z is %state% >> state.txt
)))
But, the result concludes that every system is 'UP' even though some IP addresses are unused.
Any problem with the script?
Yes. The standard delayedexpansion problem. Many, many, many, many examples here on SO about this.
Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed - the same thing applies to a FOR ... DO (block).
Hence, IF (something) else (somethingelse) will be executed using the values of %variables% at the time the IF is encountered.
Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.
Further, because your batch neither resets state when it terminates nor uses a setlocal to automatically clear changes to the environment on termination, on subsequent running, the previously-set value of state is used (hence the very first time, you'd get echo is off and thereafter the state of the last server scanned)
IF NOT ERRORLEVEL 1 (set state=UP) ELSE (set state=DOWN)
Oh! the fuzzy thinking here - and it's becoming prevalent, not just common. What's wrong with
IF ERRORLEVEL 1 (set state=DOWN) ELSE (set state=UP)
which is far more straight-forward and doesn't involve double-negatives?
Solution 1:
#echo off
setlocal enabledelayedexpansion
for /L %%x in (18,1,25) do (
for /L %%y in (1,1,254) do (
for /L %%z in (1,1,254) do (
ping -n 1 172.%%x.%%y.%%z | find "Reply" > NUL
IF ERRORLEVEL 1 (set state=DOWN) ELSE (set state=UP)
echo 172.%%x.%%y.%%z is !state! >> state.txt
)))
Solution 2:
#echo off
for /L %%x in (18,1,25) do (
for /L %%y in (1,1,254) do (
for /L %%z in (1,1,254) do (
ping -n 1 172.%%x.%%y.%%z | find "Reply" > NUL
(
IF ERRORLEVEL 1 (echo 172.%%x.%%y.%%z is DOWN
) ELSE (echo 172.%%x.%%y.%%z is UP
)
)>> state.txt
)))
Ping returns an errorlevel by itself these days:
#echo off
for /L %%x in (18,1,25) do (
for /L %%y in (1,1,254) do (
for /L %%z in (1,1,254) do (
ping -n 1 172.%%x.%%y.%%z > NUL
IF NOT ERRORLEVEL 1 (
>> state.txt echo 172.%%x.%%y.%%z is UP
) else (
>> state.txt echo 172.%%x.%%y.%%z is DOWN
)
)))

run a batch file for every alive machine in a /24 network

The script in its current form
#echo on
setlocal EnableDelayedExpansion
set /p ipAddress="enter ip address: "
rem right now the loop is set to (1,1,50) for the sake of testing
for /l %%i in (1,1,50) do (
ping -n 1 %ipAddress%.%%i | find "TTL" > nul
if !errorlevel! == 0 (
deploy_mir.bat %ipAddress%.%%i
)
)
endlocal
and then the result of running it on a known to be online host (10.167.194.22) is
C:\DOCUME~1\socuser2\MIR>test.bat
C:\DOCUME~1\socuser2\MIR>setlocal EnableDelayedExpansion
C:\DOCUME~1\socuser2\MIR>set /p ipAddress="enter ip address: "
enter ip address: 10.167.194
C:\DOCUME~1\socuser2\MIR>for /L %i in (22 1 50) do (
ping -n 1 10.167.194.%i | find "TTL" 1>nul
if !errorlevel! == 0 (deploy_mir.bat 10.167.194.%i )
)
C:\DOCUME~1\socuser2\MIR>(
ping -n 1 10.167.194.22 | find "TTL" 1>nul
if !errorlevel! == 0 (deploy_mir.bat 10.167.194.22 )
)
"Mir Agent deployment to: 10.167.194.22"
Now that last line there means that !errorlevel! == 0 (ie, "TTL" was indeed found)
so up to this point it looks like the script is working. However on the next loop, 10.167.194.23 (alive) got skipped as well as .30 and .46 . I decided to add
echo %errorlevel%
at the end of the loop to see whats going on here. Apparently, after every ping %errorlevel% was 0 so clearly
ping -n %ipAddress%.%%i | find "TTL" >nul
is where my issue is. According to this statement, "TTL" is being found after every ping, which is false, between 10.167.194.22-.50 only 3 machines are alive.
by the way, when i do
!errorlevel! == 0
what does that mean?
Everything below this line is as of 4/26/12
So my new script looks like this
#echo on
set /p ipAddress="enter ip address: "
set i=
for /l %%i in (1,1,255) do (
ping -n 1 %ipAddress%.%%i> test.txt
find "TTL" test.txt
if %errorlevel% == 0 (
deploy_this.bat %ipaddress%.%%i
)
i first tried the script without the if errorlevel check,
and it worked fine. It started pinging the ip address that i provided and proceeded to .2 .3 .4 .5 and so on.
once i added this however...
if %errorlevel% == 0 (
deploy_this.bat %ipaddress%.%%i
)
this is what happens
C:\DOCUME~1\socuser2\MIR>test.bat
C:\DOCUME~1\socuser2\MIR>set /p ipAddress="enter ip address: "
enter ip address: 10.167.201
C:\DOCUME~1\socuser2\MIR>set i=
C:\DOCUME~1\socuser2\MIR>
and the script just stops dead. any ideas?
There are a few issues :
A batch file will never return unless you use the call statement
You are missing a parenthesis
Inside a FOR loop, you must enabledelayedexpansion and use !ERRROLEVEL!
I suggest you look at this code, it has a few improvements :
It use setlocal to keep its variable to itself
It does not produce a temp file
Can take the ip address from the command line or prompt for it
It is indented
It is less verbose in its output
Here it is :
#echo off
setlocal EnableDelayedExpansion
set ipAddress=%1
if "%ipAddress%"=="" SET /P ipAddress="enter ip address: "
for /l %%i in (1,1,2) do (
rem Remove the > nul at the end if you want to
rem see the ping results in the output
ping -n 1 %ipAddress%.%%i | find "TTL" > nul
if !ERRORLEVEL! == 0 (
call deploy_this.bat %ipAddress%.%%i
)
)
endlocal
Perhaps I would rewrite your loop like this:
FOR /L %%i IN (1,1,50) DO (
ping -n 1 %ipAddress%.%%i | find "TTL" >NUL && (
CALL deploy_mir.bat %ipAddress%.%%i
)
)
Language-independent ping reply analysis would be like below:
ping -n 1 %ipAddress%.%%i
if errorlevel 1 goto :dead

Resources