Bash 等待 ping 成功

der*_*ugo 15 bash scripts ping

我正在编写重新启动各种服务器的脚本。重新启动后,我想“等待”直到所有服务器重新联机。(为了简单起见,我在网上为我定义了=可ping通)

所以对于我做的每台服务器

ServerXY_W=1
echo -n "waiting for ServerXY ..."
while (($ServerXY_W == 1))
do
   if ping -c 1 -w 0.2 192.168.123.123 &> /dev/null
   then
      echo "ServerXY is back online!"
      ServerXY_W=0
   else
      echo -n "."
   fi
done
Run Code Online (Sandbox Code Playgroud)

我期望(和喜欢)将是一个输出,例如

waiting for ServerXY .................
ServerXY is back online!
Run Code Online (Sandbox Code Playgroud)

点.... 会一一出现的地方。

但实际发生的是首先只有

waiting for ServerXY ...
Run Code Online (Sandbox Code Playgroud)

有一段时间,当服务器回来时,我得到最后一个点和最后一行

waiting for ServerXY ....
ServerXY is back online!
Run Code Online (Sandbox Code Playgroud)

为什么while循环只执行两次,一次ping失败,一次ping成功?我必须更改什么才能在 while 循环中添加更多点?

我也用不存在的 IP 进行了测试。但它卡住了

waiting for NonExistentServer...
Run Code Online (Sandbox Code Playgroud)

当然永远不会终止。但同样的问题为什么不........添加?

Ser*_*nyy 15

问题

问题是你已经设置了-w 0.2. 当 value 低于 1 时,deadline ( -w) 和 timeout ( -W) 值将被忽略。这在这个问题之前已经提到过。当您使用 时-w 1,您的脚本(我稍微修改以删除无用的位)正常工作:

$ ./ping_server.sh                                                 
waiting for ServerXY ....................
Server is back online

$ cat ./ping_server.sh
#!/bin/bash
printf "%s" "waiting for ServerXY ..."
while ! ping -c 1 -n -w 1 147.153.237.192 &> /dev/null
do
    printf "%c" "."
done
printf "\n%s\n"  "Server is back online"
Run Code Online (Sandbox Code Playgroud)

解决方案

明显的解决方案是使用-w 1. 如果您确实打算使用低于 1 秒的值,则该timeout命令应该更好:

$ timeout 0.2 ping -c 1 147.153.237.192                            
PING 147.153.237.192 (147.153.237.192) 56(84) bytes of data.
64 bytes from 147.153.237.192: icmp_seq=1 ttl=124 time=2.61 ms

--- 147.153.237.192 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 2.612/2.612/2.612/0.000 ms
Run Code Online (Sandbox Code Playgroud)

同样,!在循环中将它与运算符一起使用:

$ ./ping_server.sh                                                 
waiting for ServerXY ....................
Server is back online

$ cat ./ping_server.sh
#!/bin/bash
printf "%s" "waiting for ServerXY ..."
while ! ping -c 1 -n -w 1 147.153.237.192 &> /dev/null
do
    printf "%c" "."
done
printf "\n%s\n"  "Server is back online"
Run Code Online (Sandbox Code Playgroud)

当然,相反的方法可以应用于仅在服务器启动时显示消息并在服务器关闭时报告,例如:

$ while ping -q -c 1 172.16.127.2 >/dev/null ; do sleep 1; done ; echo "Server stopped responding"
Server stopped responding
Run Code Online (Sandbox Code Playgroud)

但是请注意,这并不完美:

  • 我们每秒只 ping 1 个数据包。带宽低、连接性差、服务器和客户端之间的硬件不好 ping 服务器会触发循环退出并发出误报通知

  • 我们依靠 ping,即使用 ICMP 回声。防火墙甚至单个服务器都会阻止对 ping/ICMP 回显的响应。你可以使用ncncat(这是一个改进版本nc)。上面循环中的类似内容将正常工作,而不是ping

    nc -w5 -z 172.16.127.2 80
    
    Run Code Online (Sandbox Code Playgroud)

    这样做是在端口 80 上连接到 172.16.127.2 上的服务器。-z是为了避免 I/O - 只需连接和断开连接。-w是等待 5 秒才报告连接失败。当然,当您控制服务器并且知道端口 80 已打开时,这非常有用。UPD 可以很好地使用,但如果有防火墙,TCP 可能是首选。

    这里的一个隐藏好处是,如果您在特定端口上运行某些服务(例如端口 80 上的 HTTP 或 554 上的 RTSP ),则无法连接到端口可能表明您的服务需要重新启动。

  • 当然,nc而且ping可能有点垃圾邮件。更好的方法是让服务器与另一台中央服务器签入,发送定期报告,也许每小时一次;这样,如果您的服务器错过了“打卡时间”,您就会产生错误。更好的方法是使用 Nagios 之类的服务,它可以做到这一点。但在这一点上,我们正在进入具有多台服务器的企业级计算领域。如果你家里有树莓派之类的东西,你可能不需要任何复杂的东西。