Reputation: 37
I'm trying to figure out a way to get a batch file to respond differently based on the results of a series of 4 pings. If the pings have a 0% loss, a web page should open. If the command returns a loss % higher than 0, then a goto command should initiate.
The things I've attempted already are changing the "else" in my code to if "%errorlevel%"=="4" or if "%errorlevel%"=="100"
:login3
cls
echo.
echo Login information cleared!
echo Press any key to confirm internet connection.
pause > nul
ping 8.8.8.8
if "%errorlevel%"=="0" start "" https://stackoverflow.com
else goto :login_fail
cls
echo.
echo test
echo if you got connected to the website, then you should end up here.
pause
exit
:login_fail
cls
color 0c
echo.
echo Login failed. Try again later.
pause > nul
If I have an internet connection or not, I will always get sent to the test message, and the website will attempt to open.
Upvotes: 1
Views: 628
Reputation: 3264
Find
will meet your need.( ( PING 8.8.8.8 | FIND /I "Reply From 8.8.8.8" >Nul ) && ECHO(Success! ) || ECHO(Failure!
Here is your original Code using this method:
:login3
cls
echo.
echo Login information cleared!
echo Press any key to confirm internet connection.
pause > nul
(
PING 8.8.8.8 | FIND /I "Reply From 8.8.8.8" >Nul && (
start "" https://stackoverflow.com
cls
echo.
echo test
echo if you got connected to the website, then you should end up here.
pause
)
) || (
cls
color 0c
echo.
echo Login failed. Try again later.
pause > nul
)
Exit /b
The && allows you to test for success, and the || tests fail, and since Find will just report success or fail once, we don't have to worry how many times we are testing (although you might have reason to test more than 4 or less than 4 pings we'll just work with that for now)
This means that If we match the reply and it has the original IP we looked for, then we know it must be a reply from the host we are pinging, and we'll get a success, and execute everything in the && block.
However, if we NEVER match the expected result, then Find will return a fail and we'll skip the && block and execute everything in the || block.
Ahh, this is the sort of thing that comes up all the time as needing to be done, and it is very easy to handle you know the caveats.
On outset, you would think to match % Loss One and done right?
Eg:
REM For /F Example
FOR /F "Tokens=2 Delims=(%%" %A in ('ping 8.8.8.8') DO @( IF /I %%~A GTR 0 ( ECHO. OKAY! ) ELSE ( ECHO.Failed! ) )
REM Find Example
( ( ping 8.8.8.8 | FIND /I "(100%" >nul ) && Echo.Failed! ) || Echo.Success!
Well when you have a destination that can't be reached by a router between you and the IP, the Router will sometimes reply with "Destination Host Unreachable
" (This may happen every ping, one in every X pings, or never, it depends on how the router is configured.)
This is an actual ICMP Reply and so internally Ping is just keeping track of actual responses, and displaying their results, so the % Loss will not be 100%. Destination Host unreachable will count as replies, because the router is replying to you.
Reply from 10.41.3.232: Destination net unreachable.
This means you can also not simply count a match of "Reply" via For /F
Loop or Find
/FindStr
.
However, as you may have noticed from the example above, the Router's Ip is returned in these cases, so the unique Reply from you Do wish to count is always going to contain the source IP.
So for a simple Check such as yours, the following Find
will meet your need.
( ( PING 8.8.8.8 | FIND /I "Reply From 8.8.8.8" >Nul ) && ECHO(Success! ) || ECHO(Failure!
If you prefer you can call the functions or net the code inside the && and || Sections
In addition, how do you handle the case of using a hostname/FQDN to ping instead of the IP?
Well here you will want to use a For Loop to grab the IP response from the Ping Command, then use it in a further test.
This can be done in-line if Delayed Expansion is enabled, or will need some parsing in a function if not. But this also leads to what to do if you want to have more than one response depending on how many replies you receive.
As once you are iterating over the results you won't want to have more than one success fire, so its simplest just to count the number of replies that matched anyway, and then check if greater than 0 as it's a trivial difference.
Check when the Address to check is a name not an IP
@(
SetLocal EnableDelayedExpansion
ECHO OFF
SET "_Count=0"
)
FOR /F "Tokens=1-2 Delims=[]" %%A IN ('Ping www.aol.com') DO (
IF /I "%%B" NEQ "" (
SET "_IP=%%B"
) ELSE (
ECHO.%%A | FIND /I "Reply From !_IP!" >nul && SET /A "_Count+=1"
)
)
IF /I %_Count% GTR 0 (
Echo.Success!
) ELSE (
Echo.Failure!
)
This allows you to have more than Just success/Fail states if you want to do something different depending on the number of replies returned. As noted above to not bother keeping count and have only success or failed will be nearly Identical.
However, it only works as above when the initial Item was NOT an IP.
To do the above for an IP is essentially the same as the original case but just to be thorough here is that:
Check when the IP is given and keep Count
@(
SetLocal EnableDelayedExpansion
ECHO OFF
SET "_Count=0"
)
FOR /F "Tokens=*" %A IN ('PING 8.8.8.8 ^| FIND /I "Reply From 8.8.8.8"') DO (
SET /A "_Count+=1"
)
IF /I %_Count% GTR 0 (
Echo.Success!
) ELSE (
Echo.Failure!
)
But this ONLY works when it IS an IP.
But, okay now you are probably wondering how to handle both, and I feel like I really should address that on some level.
So if you do two pings, you can check to see if the Ping resolved the name through DNS, however, on any given DNS Query it's possible you might get a different IP address.
Well it's possible to force any IP that is resolvable to show the brackets by using Ping -a
in your test then you can use the bulkier command.
C:\Admin>ping -a 8.8.8.8
Pinging dns.google [8.8.8.8] with 32 bytes of data:
However, this does not account for IPs that have no reverse pointer but may still be pingable.
C:\Admin>ping -a 10.10.172.1
Pinging 10.10.172.1 with 32 bytes of data:
So if you do two pings, you could check to see if the Ping resolved the name through DNS, however, on any given DNS Query it's possible you might get a different IP address, so while we could have a two loops, and have one set to say 1 ping just to see if it resolves, we can't use the result and forgo a second loop, and even if we set ping to 1 response we are making the process longer.
So it would be better not to have an unneeded call to the ping command, especially since some scenarios take a long time to respond.
And to do that we should change how we approach setting the _IP
Variable slightly, to accomplish both of our goals with only one loop and one ping command.
To do this we change our logic to see if the _IP
variable is defined, if it isn't we check to see if the second term found something (IE that the braces with the IP in them were encountered) in which case we can use that IP, and if that term is empty, we'll assume that the originally given address was an IP and not a name.
Note: In the case where it was a name, but the DNS did not resolve it, we can not ping it anyway, and Ping will throw only a single error message for this then exit, so we do not need to worry about this value being a name instead of an IP.
In this scenario test to see if our variable _IP
has been defined yet.
@(
SetLocal EnableDelayedExpansion
ECHO OFF
SET "_PingAddress=www.aol.com"
SET "_Count=0"
)
FOR /F "Tokens=1-2 Delims=[]" %%A IN ('Ping %_PingAddress%') DO (
IF NOT DEFINED _IP (
IF /I "%%B" NEQ "" (
SET "_IP=%%B"
) ELSE (
SET "_IP=%_PingAddress%"
)
) ELSE (
ECHO.%%A | FIND /I "Reply From !_IP!" >nul && SET /A "_Count+=1"
)
)
IF /I %_Count% GTR 0 (
Echo.Success!
) ELSE (
Echo.Failure!
)
This is also a bit cleaner than the original test for the Address IMO, and handles this scenario of IPs.
Upvotes: 0
Reputation: 56228
The errorlevel of ping
is useless. It (sadly) doesn't tell you anything.
You can either extract the "loss percentage" (which is also useless, as @Mofi already pointed out) or do it yourself: count the occurrences of successful returns (and do some math if needed):
@echo off
setlocal
set "addr=8.8.8.8"
set tries=4
for /f %%a in ('ping %addr% /n %tries% ^|find /c "TTL="') do set "ret=%%a"
if %ret%==%tries% (goto :open_url) else (goto :login_fail)
REM some math to get the percentages, should anyone have use for it:
echo Returns/Tries = %ret%/%tries%
set /a succ=ret*100/tries
set /a loss=100-ret*100/tries
echo loss=%loss%; success=%succ%
goto :eof
:open_url
REM insert code
goto :eof
:login_fail
REM insert code
goto :eof
Upvotes: 1