Batch file - variable won't update when assigned a new value - batch-file

I have a script that simply pings a box, and if the box replies, it changes a variable called "newstate" to up. Otherwise it gets set to "down". For some reason, when I run it on my machine, newstate never gets changed to "up" even though it's online and replies to pings.
I'm probably missing something very small, can you please help? (I also put in a variable to log status when it changes, but I didn't implement it yet because I can't get the switching to even be recognized). Ultimate goal is to log when status changes from up to down or vice versa.
EDIT - Changed %newstate% to !newstate!, still not working.
#setlocal enableextensions enabledelayedexpansion
#echo off
title loop test
set system=192.168.0.155
set oldstate=up
set newstate=down
:loop
echo.
echo ...........
echo pre check
echo old state is %oldstate%
echo new state is !newstate!
echo ...........
echo.
:::::: CONNECTIVITY CHECK ::::::
for /f "tokens=5,7" %%a in ('ping -n 1 -4 !system!') do (
if "x%%a"=="xReceived" if "x%%b"=="x1," set newstate=up
)
echo new state is !newstate!
if !newstate! neq up set newstate=down
set log=%time% - %sytem% - !newstate!
if %oldstate% neq !newstate! set oldstate=!newstate!
echo.
echo ...........
echo post check
echo old state is %oldstate%
echo new state is !newstate!
echo ...........
echo.
:::::: DELAY LOOP ::::::
ping -n 10 127.0.0.1 >nul: 2>nul:
goto :loop
endlocal

Well , it worked for me, but since i work with a spanish copy of windows i had to change the Received literal and use recibidos. Not sure about your local configuration.
It is usual better to check for the presence of the TTL= value into the ping response. If it is found, the target is up, else, it is down
ping -n 1 192.168.0.155 | find "TTL=" >nul
if errorlevel 1 (
set "newstate=down"
) else (
set "newstate=up"
)
So, with a little simplification,
#echo off
setlocal enableextensions disabledelayedexpansion
title loop test
set "system=192.168.1.1"
set "oldState=down"
set "newState=up"
:loop
ping -4 -n 1 %system% | find "TTL=" >nul && set "newState=up" || set "newState=down"
if %newState%==%oldState% (
echo %time% - %system% still %oldState%
) else (
echo %time% - %system% goes %newState%
set "oldState=%newState%"
)
ping -n 10 127.0.0.1 >nul 2>nul
goto loop
Where the ping line should be read as "ping , find ttl= in the output, if found set newState to up, else set newstate to down"
EDITED -
This was the usual way of testing for ping response. In IPv4. But when pinging in IPv6, the TTL field is not shown, so we can not check it.
The original reason for testing the TTL= in the output of the ping command was the way packet counts and errorlevels are handled. If a packet is lost, errorlevel is set. If no packet lost, no errorlevel. But, pinging a non active machine in the same subnet generates a 'unreachable' response and no packet lost, so, no errorlevel and packet count reflects 0 packet lost. And, ...
Also, almost all the information in the ping output is localized, making it difficult to check the result of the ping command and make it consistent across different OS localizations.
But some things have changed in IPv6 ping. If requested machine is not reachable, packets are lost and errorlevel is set. In same subnet or not.
And, if some packets are lost, but at least one packet is not lost, errorlevel is not set.
So, when working with IPv4, the test for TTL= presence in ping outout is a "reliable" way to check the state of the remote machine.
And, when working with IPv6, errorlevel reflects the success of failure of the process.
And, please, note that i'm not a network guru. This is what i have seen when testing. If i'm wrong, please, comment.

Use !newstate! to get variable value, instead of %newstate%. This is the way of getting delayed variables values.
Keep using "setlocal enabledelayedexpansion".

The problem is in this statement
if "x%%a"=="xReceived" if "x%%b"=="x1," set newstate=up
which means if firstcondiion and secondcondition set ...
So - i'd insert a debug line
echo +%%a+%%b+
just before this if statement, and observe results. The + is just there as an obvious character to make seeing Spaces easier.
Remember - if is case-sensitive, so if you need case-insensitive, use if /i
Also - observe Spaces as a quoted-string in an if incudes any spaces in the match.
There is no point in delayedexpansion in this instance, since you are not attempting to read a values that changes within (a parenthesised block of statements). Under these circumstances, %var%==!var!

Related

I need to write a batch script that will restart two services, but only start the second service after checking the log file

I need to write a batch script that will restart two services, but it will need to check the log file of the first service for a specific string value before restarting the second service, keep looping back until the sting value is found before starting the second service.
I have very little experience with batch scripting, but I think I need to somehow assign the string value to Var and only if it's the correct value start the second service?
#echo off
sc \localhost Stop IMS01
sc \0.0.0.0 stop IMS02
sc \localhost Start IMS01
rem delay of 4mins
ping 127.0.0.1 -n 240 > nul
:Loop
IF EXIST Find "started and accepting connections" D:\logs\IMS.log
GOTO StartIMS02
IF NOT EXIST Find "started and accepting connections" D:\logs\IMS.log
Goto Loop
:StartIMS02
sc \0.0.0.0 Start IMS02
May I assume that all of your 0.0.0.0 are placeholders for known working code and you just need help with the parsing of the log file? If so:
#(
echo off
SETLOCAL EnableDelayedExpansion
SET "_Svr1=localhost"
SET "_Svc1=IMS01"
SET "_Svr2=0.0.0.0 "
SET "_Svc2=IMS02"
SET "_Delay=240"
SET "_Delay_Loop=30"
SET "_StringToMatch=started and accepting connections"
)
FOR /L %%L IN (1,1,2) DO (
ECHO.Stopping: !_Svc%%L! On: !_Svc%%L!
SC \\!_Svr%%L! Stop !_Svc%%L!
)
ECHO.Starting: %_Svc1% On: %_Svr1%
SC \\%_Svr1% Start %_Svc1%
rem delay of 3 minute(s)
PING 127.0.0.1 -n %_Delay% > nul
:Loop
rem delay of 1 minute(s)
PING 127.0.0.1 -n %_Delay_Loop% > nul
IF EXIST "D:\logs\IMS.log" (
(
Find /I "%_StringToMatch%" "D:\logs\IMS.log"
) && (
ECHO.
ECHO.============================
ECHO.Success! Found the String "%_StringToMatch%"
ECHO.
ECHO. Starting %_Svc2% on %_Svr2%
SC \\%_Svr2% Start %_Svc2%
) || (
ECHO. Failed to Find the String "%_StringToMatch%"
ECHO. Looping, will wait another %_Delay_Loop% Seconds before testing again.
GOTO :Loop
)
) ELSE (
ECHO Log File Not Found! Aborting!
)
GOTO :EOF
Now I decided to set a bunch of variable names just for my own sanity, that is all optional, although it does mean we can loop the two services to stop them.
The Logic I have put in place checks if the log file exists, if it does not it aborts the script.
It also adds a 30-second wait on the looping, you can play around with those a lot easier when they are variables IMHO.
I put in some ECHOs to let you keep tabs on what the script is doing they are all optional.
Now the meat of it is I use the FIND /I "String" command to check for the string, a dif it exists we'll start service 2. && is a logical test to see if the result was a success, while as you may have guessed, || tests for a fail state. We have to test for success first or the actions of the fail state could theoretically trigger the success state to run too. (although in your specific case the GoTo would break that from happening.)
If the string was not matched w loop again and we'll have our 30 second delay before retry.

Better way to use findstr to get packet loss, batch

Looking to use findstr to get it to find "0% loss" after a ping command.
Perhaps an array of anything up to "50% loss".
This is for checking and ensuring a connection to the internet is 100% established before launching something online.
Currently it's structured:
ping %ip% -n 3 -w 3000 | findstr "0% loss"
pause
goto Starting
It's currently ignoring findstr and no matter what it refuses to find what I'm looking for
Ideally it would flow like:
ping %ip% -n 3 -w 3000 | findstr "0% loss" || goto Echo
pause
goto Starting
:echo
Could not find "0% loss"
pause
And I have tried that, it will go to echo, but even with 100% connection so it's clearly just not operating how I'd like it to.
Is there a better way to find % packet loss?
Or
Is there a better way to test internet connection, given ping doesn't work when a device is totally offline.
The search string you are looking for is too broad. When you check findstr for "0% loss", you are inadvertently picking up "100% loss" as well. Fortunately, ping puts the packet loss in parentheses, so you can simply include the open parenthesis in the search string.
#echo off
title Restart
color 0A
cls
:start
Cls
set ip=www.google.com
:Pingcheck
echo Checking ping..
timeout /t 3
ping %ip% -n 5 -w 3000 | findstr /C:"(0% loss" || goto Loss
pause
:NoLoss
echo We found 0 packet loss, at %ip% on %date% at %time%
pause
goto start
:Loss
echo We found some packet loss.
pause
I've also changed the name of the :Echo label because echo is already a command and having it also be a label would be confusing.
You can also check ping status with wmi. The advantage to this method is that it'll goto :fail on the first failure, and not continue its lost cause of completing 5 ping attempts, while also providing a simple way to preserve the average response time. It uses set /a to check whether the result of the wmi query contains a numeric value. If it does, pass. If not, fail.
#echo off & setlocal
set "host=www.google.com"
echo Pinging %host%...
for /L %%I in (1,1,5) do (
for /f "delims=" %%x in (
'wmic path win32_pingstatus where "address='%host%' and timeout=3000"^
get ResponseTime /value ^| find "="'
) do (
2>NUL set /a "%%x, avg += ResponseTime" || goto fail
)
)
set /a "avg /= 5"
echo 0%% packet loss. Woo. Average response time was %avg%ms.
exit /b
:fail
echo Aw snap. Packet loss.

How to parse text from an output in Batch

So i was tasked to a match a batch script that would ping an ip then print if a response was there or not and what I am attempting to do is to first ping, then save it on a text file,then use an if statement so if the string contains the text Request was timed out it would display that the IP is unavailable.
This is my example code
#echo off
cls
echo Executing ping....
ping 123.456.789 >> output.txt
pause
echo Here are the results:
findstr "Request" output.txt
if %finstr% == "Request" {
echo IP does exist
}
else {
echo IP does exist
}
I was trying to use findstr to get the string but that would involve saving the string and comparing it but I have no idea how to do that. You get the jist of it. Thanks and all help is appreciated very much.
Post scriptum: the if statement is what i think it looks like since i don't ever do batch programming i am a newbie
I'd suggest that there's a far better approach; ERRORLEVEL.
#echo off
ping -n 1 192.168.1.1 > NUL
if ERRORLEVEL 1 (
echo Host is down.
) else (
echo Host is up.
)
If ping succeeds, ERRORLEVEL will be zero. If it fails, ERRORLEVEL will be 1 (or possibly greater if it fails for other reasons, I suppose... if ERRORLEVEL 1 tests for ERRORLEVEL >= 1).

Sending a command to a range of IP's using PSEXEC and a Batch file

I'm trying to send an unlock command to remote computers. All computers are on the same subnet and an upper and lower IP will be provided at run time.
I have constructed the following:
:range
echo.
set /a ip=xxx.xxx.xxx
echo Please input lower IP range.
set /p lower=%ip%
echo.
echo Please input upper IP range.
set /p upper=%ip%
set /a lip=
echo.
:loop
set lip = %ip%%lower%
psexec \\%lip% -u .\<redacted> -p <redacted> Net user <redacted> /Active:yes
if %lower%==%upper% goto loopend
set /a lower=%lower%+1
goto loop
:loopend
goto end
This gives me a lot of errors. Firstly, I understood set lip = %ip%%lower% would concatenate the two. I'm not certain they are being processed as strings, however. How can this be resolved?
If I echo %ip% I only see the first part of the IP (before the first .). Clearly this data isn't being stored correctly. I tried enclosing it in speech marks with no success (it says Missing operator).
I am open to any radically different solutions if you feel I am making a pigs ear of this.
You need to add a . between the parts of your IP address.
set lip=%ip%.%lower%
You can add an ECHO statement right after the above line to make sure the IP looks right.
ECHO %lip%
UPDATE
You also need to remove the /a from this line:
set /a ip=xxx.xxx.xxx
It should just be:
set ip=xxx.xxx.xxx

Pause a batch file until a host is reachable (using ping)?

I'm looking to add some functionality to a batch file that I've been writing;
essentially what happens is I dial up a VPN connection using openvpn and then continue to mount a network drive as well as many other things, what I'm looking to do is:
Dial the connection via OpenVPN (which I have working fine)
Ping a host on the other side of the VPN and don't continue through the batch file until this host is reachable.
Currently I've been using a sleep command of 20 seconds which works, but is not a very clean or intelligent way of going about it; I'd imagine I need some sort of loop to attempt to ping the host infinitely until it is reachable before continuing in the batch file. Any help would be greatly appreciated.
from antoher thread on stackoverflow... credit to paxdiablo (find the original post here)
#setlocal enableextensions enabledelayedexpansion
#echo off
set ipaddr=%1
: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 127.0.0.1 >nul: 2>nul:
goto :loop
endlocal
This will give you enough ammo to use and solve your problem
as already mentioned years ago, output of ping is language dependent, so it's not a good idea to rely on a string like Received. The preferred and most reliable method is searching for the string TTL=:
:loop
timeout 2
ping -n 1 %ipaddress% |find "TTL=" || goto :loop
echo Answer received.
|| works as "if previous command (find) wasn't successful, then"
(as for the timeout: never build a loop without some idle time to reduce CPU load)
It works if You translate the "Received" string using the string that your language uses for the word Received (i.e. in Italian is Ricevuti).

Resources