如何在批处理文件中检查ping是否有响应

54
我希望能够通过批处理文件持续对服务器进行ping操作,并在服务器响应时显示一个消息框,即服务器目前不可用。我想通过批处理文件实现此操作。
我可以像这里所说的那样显示一个消息框:Show a popup/message box from a Windows batch file 并且可以通过以下方式持续进行ping操作:
ping <servername> -t

但是我该如何检查它是否已经响应?

它通常不会返回 xx 字节的数据吗? - Sphvn
但我想知道如何在批处理文件中的if条件语句中检查它。 - IsmailS
这个问题已经变得很受欢迎(被浏览了1000次),但是问题和答案的赞数并不多。这让我猜想,访问者来到这个页面是为了其他事情,而在这里没有找到。换句话说,我猜测这个页面出现在一些与此问题无关的关键词的搜索结果中。我想知道那些其他事情(或与此问题无关的关键词)是什么,以及如何更改这个问题的标题,以便访问者(期望其他内容的人)节省时间。 - IsmailS
13个回答

34

这个问题是想要查看 ping 命令是否有响应,而这个脚本可以实现。

然而,如果你收到了“目标主机不可达”的消息,这个脚本就不能正常工作了。因为它返回 ERRORLEVEL 0 并且符合本脚本中用于检查接收 = 1 的条件,从而返回“连接已建立(Link is UP)”。当 ping 命令传递到目标网络但无法找到远程主机时,就会出现“目标主机不可达”的情况。

如果我没记错的话,检查 ping 命令是否成功的正确方法是使用 Find 查找字符串 'TTL'。

@echo off
cls
set ip=%1
ping -n 1 %ip% | find "TTL"
if not errorlevel 1 set error=win
if errorlevel 1 set error=fail
cls
echo Result: %error%

这不适用于IPv6网络,因为ping在从IPv6地址接收回复时不会列出TTL。


1
使用ping -4参数强制使用IPv4。非常好的解决方案,谢谢! - jacobsee

29
下面的checklink.cmd程序是一个很好的起点。它依赖于这样一个事实,即您可以进行单次ping操作,并且如果成功,则输出将包含以下行:
The following checklink.cmd program is a good place to start. It relies on the fact that you can do a single-shot ping and that, if successful, the output will contain the line:
Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),
通过提取第5个和第7个令牌,并检查它们是否分别为"Received""1,",您可以检测到成功。
@setlocal enableextensions enabledelayedexpansion
@echo off
set ipaddr=%1
:loop
set state=down
for /f "tokens=5,6,7" %%a in ('ping -n 1 !ipaddr!') do (
    if "x%%b"=="xunreachable." goto :endloop
    if "x%%a"=="xReceived" if "x%%c"=="x1,"  set state=up
)
:endloop
echo.Link is !state!
ping -n 6 127.0.0.1 >nul: 2>nul:
goto :loop
endlocal

使用你想要测试的名称(或IP地址)调用它:

checklink 127.0.0.1
checklink localhost
checklink nosuchaddress

请注意,如果你的语言环境不是英语,你需要用你的语言环境中相应的关键字替换Received,例如在西班牙语中使用 recibidos。进行测试ping以发现在你的语言环境中使用的关键字。


若要仅在状态更改时通知您,可以使用:

@setlocal enableextensions enabledelayedexpansion
@echo off
set ipaddr=%1
set oldstate=neither
: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
)
if not !state!==!oldstate! (
    echo.Link is !state!
    set oldstate=!state!
)
ping -n 2 127.0.0.1 >nul: 2>nul:
goto :loop
endlocal

然而,正如Gabe在评论中指出的那样,您可以使用ERRORLEVEL,因此上述第二个脚本的等效版本变为:

@setlocal enableextensions enabledelayedexpansion
@echo off
set ipaddr=%1
set oldstate=neither
:loop
set state=up
ping -n 1 !ipaddr! >nul: 2>nul:
if not !errorlevel!==0 set state=down
if not !state!==!oldstate! (
    echo.Link is !state!
    set oldstate=!state!
)
ping -n 2 127.0.0.1 >nul: 2>nul:
goto :loop
endlocal

3
你所做的工作量很大,而不仅仅是检查%ERRORLEVEL% - Gabe
1
这是一个很好的观点,@Gabe,我已经将它融入到脚本中了。 - paxdiablo
7
ping 收到“目标主机不可达”的回复时,似乎会返回 %errorlevel% 为0。 - Owen
@paxdiablo,您可以解释每行代码吗? - John Alexander Betts
1
不错的解决方案,但在使用其他语言系统时,输出会被翻译。因此无法检查“Received”。我想检查这三个数字是否为1、1和0也可以起作用。 - sanderd17
显示剩余5条评论

9

我知道这是一个老的线程,但我想测试一台机器是否在我的系统上运行,除非我误解了,以上所有方法都不起作用,如果我的路由器报告某个地址不可达。我使用批处理文件而不是脚本,因为我希望在几乎任何WIN机器上都能够"KISS"。所以我采用的方法是进行多次ping,并按如下方式测试"Lost = 0"

ping -n 2 %pingAddr% | find /I "Lost = 0"  
if %errorlevel% == 0 goto OK

我还没有进行过严格的测试,但目前来看它对我很有效


2
我参考了paxdiablo的帖子并做出了一个变通方案。
请将以下代码放在Waitlink.cmd中。
@setlocal enableextensions enabledelayedexpansion
@echo off
set ipaddr=%1
:loop
set state=up
ping -n 1 !ipaddr! >nul: 2>nul:
if not !errorlevel!==0 set state=down
echo.Link is !state!
if "!state!"=="up" (
  goto :endloop
)
ping -n 6 127.0.0.1 >nul: 2>nul:
goto :loop
:endloop
endlocal

例如,可以像这样从另一个批处理文件中使用它。
call Waitlink someurl.com
net use o: \\someurl.com\myshare

调用waitlink函数只有在ping成功时才会返回。感谢paxdiablo和Gabe。希望这能帮助其他人。

2

这里有一些我找到的内容:

:pingtheserver
ping %input% | find "Reply" > nul
if not errorlevel 1 (
    echo server is online, up and running.
) else (
    echo host has been taken down wait 3 seconds to refresh
    ping 1.1.1.1 -n 1 -w 3000 >NUL
    goto :pingtheserver
) 

请注意,ping 1.1.1.1 -n -w 1000 >NUL 命令会等待1秒钟,但只有在连接到网络时才能正常工作。

1
这比许多其他方法更简单,而且对我很有效。除了我将“find”中的“Reply”更改为“time =”,因为有时它会找到“Reply from X.X.X.X:Destination host unreachable.”并将其视为成功,而我希望它是失败的。 - Truisms Hounds

1
感谢@paxdiablo和@Jan Lauridsen,这是我修改后的脚本,用于检查IP(本地机器),适用于连接断开的情况,无论是因为dhcp服务器还是其他问题,已在WinXP PRO SP3下测试通过。 checkconnection.cmd:
@setlocal enableextensions enabledelayedexpansion
@echo off
set ipaddr=8.8.8.8
:loop

for /f "tokens=5,6,7" %%a in ('ping -n 1 !ipaddr!') do (
    if "x%%b"=="xunreachable." goto :endloop
    if "x%%b"=="xtimed out." goto :endloop
    if "x%%a"=="xReceived" if "x%%c"=="x1,"  goto :up  
)

:endloop
set state=Down
echo.Connection is !state!
ping -n 2 127.0.0.1 >nul: 2>nul: 
echo starting Repairing at %date% %time%>>D:\connection.log
call repair.cmd>>D:\connection.log
ping -n 10 127.0.0.1 >nul: 2>nul:
goto :loop
endlocal
:up
set state=Up
echo.Connection is !state!
ping -n 6 127.0.0.1 >nul: 2>nul:
cls
goto :loop
endlocal

如果从Google DNS没有ping响应,则启动修复过程,我为此设置了静态IP,但与动态IP也应该可以工作。

repair.cmd:

route -f
ipconfig /release
ipconfig /renew
arp -d *
nbtstat -R
nbtstat -RR
ipconfig /flushdns
ipconfig /registerdns
cls

致以最好的祝福


0

希望这能帮助到某些人。我使用这个逻辑来验证网络共享是否响应,然后再检查各个路径。它应该处理DNS名称和IP地址。

文本文件中的有效路径应为\192.168.1.2\'文件夹'或\NAS\'文件夹'

@echo off
title Network Folder Check

pushd "%~dp0"
:00
cls

for /f "delims=\\" %%A in (Files-to-Check.txt) do set Server=%%A
    setlocal EnableDelayedExpansion
        ping -n 1 %Server% | findstr TTL= >nul 
            if %errorlevel%==1 ( 
                ping -n 1 %Server% | findstr "Reply from" | findstr "time" >nul
                    if !errorlevel!==1 (echo Network Asset %Server% Not Found & pause & goto EOF)
            )
:EOF

0

简单版本:

for /F "delims==, tokens=4" %a IN ('ping -n 2 127.0.0.1 ^| findstr /R "^Packets: Sent =.$"') DO (

if %a EQU 2 (
echo Success
) ELSE (
echo FAIL
)

)

但有时候第一次ping会失败,第二次会成功(反之亦然),对吧?因此我们希望当至少收到一个ICMP回复成功时就算成功:

for /F "delims==, tokens=4" %a IN ('ping -n 2 192.168.1.1 ^| findstr /R "^Packets: Sent =.$"') DO (

if %a EQU 2 (
echo Success
) ELSE (
if %a EQU 1 (
echo Success
) ELSE (
echo FAIL
)
)

)

0
你可以在不加上“-t”的情况下进行ping操作,并检查ping的退出代码。当没有响应时,它会报告失败。

4
我们程序员可以比阅读英语更快地阅读和理解代码 ;),只是开个玩笑。 - IsmailS
2
OP可能想要设置批处理脚本运行,然后在机器上做其他事情,只有在状态改变时才会收到警报。而不是坐着观看每个ping回复结果。 - user66001

0

我稍微修改了PaxDiablo的代码,使其更适合我的需求,并想与大家分享。我的目标是循环遍历一个IP地址列表,查看是否有任何一个处于开启状态,这段代码将在周末最后一班次结束时运行,以检查每个人是否遵守了在下班前关闭所有PC的指示。

由于在for循环中使用goto会跳出所有for循环,而不仅仅是最低嵌套的for循环,因此PaxDiablo的代码在到达无法访问的IP地址时停止了处理进程。我发现我可以添加第二个变量来跟踪它是否不可访问,而不是退出循环,现在这段代码对我来说完美地运行着。

我将文件保存为CheckPCs.bat,虽然我猜测这不是正确的编码规范,但我在代码下方列出了IP地址和它们的描述,在我的情况下是PC的物理位置。正如其他人所提到的,您需要进一步修改代码以适应其他本地化和IPV6。

@setlocal enableextensions enabledelayedexpansion
@echo off
for /f "tokens=2,3 delims=/ skip=16" %%i in (CheckPCs.bat) do (
set ipaddr=%%i
set state=shut down
set skip=0
for /f "tokens=5,6,7" %%a in ('ping -n 1 !ipaddr!') do (
    if "x%%b"=="xunreachable." set skip=1
    if !skip!==0 if "x%%a"=="xReceived" if "x%%c"=="x1,"  set state=still on
)
echo %%i %%j is !state!

)
pause
endlocal

rem /IP ADDRESS0/COMPUTER NAME0
rem /IP ADDRESS1/COMPUTER NAME1

如果您需要修改此代码,请注意以下几点:

for /f "tokens=2,3 delims=/ skip=16" %%i in (CheckPCs.bat) do (

for循环逐行处理CheckPCs.bat。Skip告诉它忽略前16行,直接转到IP地址。我使用/作为分隔符,没有特定的原因,但请注意,如果您要ping网址而不是IP,您必须更改分隔符。管道字符|之类的东西会起作用。当然,由于该行被注释为rem,rem成为第一个标记,这意味着我只想使用标记2和3,即IP地址和PC描述。后一个字段是可选项,你只需要修改代码来删除它。

您应该修改用于状态和回显%%i %%j is !state!的术语,以使术语对您清晰明了。如果您想在某个地方记录状态,则可以通过附加>> file.txt将其输入到文本文件中。在那种情况下,您可能还想添加日期/时间。

最后,那些接受过编程适当培训的人可能知道,批处理循环与令牌的工作方式(简单来说)是将文本的每个部分在每个定界符处分割,默认为空格,然后将其分配给一个%%变量,其名称从您指定的任何字符开始,然后沿着ASCII字符列表递增。这意味着如果我指定从%%i开始,下一个标记将是%%j,然后是%%k等等。如果我使用%%B,则下一个将是%%C,然后是%%D等等。根据我在该主题上阅读的另一个线程,每个线程最多可以有31个令牌。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接