The "problem" with your code is the management of the errorlevel
to determine if the machine is or not online.
The questions are: how does ping
behave?, when is errorlevel
set?
If we are working with ipv6, the rules are
ipv6 has a consistent behaviour and checking the errorlevel is a reliable way to know if the machine is online.
In ipv4 the rules are different
But pinging an non available machine on the same subnet does no set the errorlevel
, you get an "unreachable" answer, with n packets sent, n packed received, 0 packets lost
, all the packets get a reply from the same machine sending the packets.
This behaviour in ipv4 when the machine is in the same subnet makes the errorlevel check fail.
How to solve the problem in ipv4?
The output of the ping
command can be checked, if the string TTL=
is present in the output, the target machine is online.
ping -n 1 10.0.0.1 | find "TTL=" >nul
if errorlevel 1 (
echo offline
) else (
echo online
)
For a "general" solution, this (adapted from a previous answer) can be used (seems a lot of code, but almost all are comments)
@echo off
setlocal enableextensions disabledelayedexpansion
if "%~1"=="" goto :eof
call :isOnline "%~1"
if not errorlevel 1 ( echo ONLINE ) else ( echo OFFLINE )
endlocal
exit /b
:isOnline address pingCount
setlocal enableextensions disabledelayedexpansion
:: send only one ping packed unless it is indicated to send more than one
set /a "pingCount=0", "pingCount+=%~2" >nul 2>nul
if %pingCount% lss 1 set "pingCount=1"
:: a temporary file is needed to capture ping output for later processing
set "tempFile=%temp%\%~nx0.%random%.tmp"
:: ping the indicated address getting command output and errorlevel
ping -w 1000 -n %pingCount% "%~1" > "%tempFile%" && set "pingError=" || set "pingError=1"
::
:: When pinging, the behaviours of ipv4 and ipv6 are different
::
:: we get errorlevel = 1 when
:: ipv4 - when at least one packet is lost. When sending more than one packet
:: the easiest way to check for reply is search the string "TTL=" in
:: the output of the command.
:: ipv6 - when all packet are lost.
::
:: we get errorlevel = 0 when
:: ipv4 - all packets are received. BUT pinging a inactive host on the same
:: subnet result in no packet lost. It is necessary to check for "TTL="
:: string in the output of the ping command
:: ipv6 - at least one packet reaches the host
::
:: We can try to determine if the input address (or host name) will result in
:: ipv4 or ipv6 pinging, but it is easier to check the result of the command
::
:: +--------------+-------------+
:: | TTL= present | No TTL |
:: +-----------------------+--------------+-------------+
:: | ipv4 errorlevel 0 | OK | ERROR |
:: | errorlevel 1 | OK | ERROR |
:: +-----------------------+--------------+-------------+
:: | ipv6 errorlevel 0 | | OK |
:: | errorlevel 1 | | ERROR |
:: +-----------------------+----------------------------+
::
:: So, if TTL= is present in output, host is online. If TTL= is not present,
:: errorlevel is 0 and the address is ipv6 then host is online. In the rest
:: of the cases host is offline.
::
:: To determine the ip version, a regular expresion to match a ipv6 address is
:: used with findstr. As it will be only tested in the case of no errorlevel,
:: the ip address will be present in ping command output.
set "exitCode=1"
>nul 2>nul (
find "TTL=" "%tempFile%" && ( set "exitCode=0" ) || (
if not defined pingError (
findstr /r /c:" [a-f0-9:][a-f0-9]*:[a-f0-9:%%]*[a-f0-9]: " "%tempFile%" && set "exitCode=0"
)
)
del /q "%tempFile%"
)
:: cleanup and return errorlevel: 0=online , 1=offline
endlocal & exit /b %exitCode%
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…