Bash循环ping成功

25

我认为这需要改成while循环,目前它会等待所有10000个ping完成,我需要在ping成功时返回。 "say"程序在OSX上用于让电脑说话。

#!/bin/bash
echo begin ping
if ping -c 100000 8.8.8.8 | grep timeout;
then echo `say timeout`;
else echo `say the internet is back up`;
fi

好的,我没有回答自己问题的权限,所以在尝试过程中,这是我的答案:

谢谢,是的,直到现在我都不知道$?。无论如何,现在我已经完成了它。我喜欢你的方法不会一直运行,但在我的情况下,我不需要它停止直到完成。

#!/bin/bash
intertube=0
echo "begin ping"
while [ $intertube -ne 1 ]; do
        ping -c 3 google.com
        if [ $? -eq  0 ]; then
                echo "ping success";
                say success
                intertube=1;
        else
                echo "fail ping"
        fi
done
echo "fin script"

这与“ping”无关,但你通过“echo”“say”想要实现什么目的?你的开头段落暗示你试图执行“say”命令,但如果你只是“echo”这个单词,那是不会发生的。 - Lawrence Velázquez
1
@ Lawrence,那些是_反引号_,而不是引号。它们将执行“say”命令并回显其输出。 - paxdiablo
1
哎呀,我错得很离谱;向你道歉。不过我仍然不确定在那里实现了什么;say从未输出任何内容到标准输出。 - Lawrence Velázquez
1
你的解决方案中不需要 $?;如果 ping …… 正常工作。此外,您可以通过使用 break 避免额外的变量:while :; do if ping; then break; fi; done - Mark Edgar
参见:ServerFault: 如何在 Linux 中 ping 直到主机被识别?。这里有一些非常有前途且简短的答案,适用于 Linux(缺少方便的 -o 选项)和 MacOs(具有该选项)。 - Gabriel Staples
7个回答

41
当你使用命令的文本输出作为决策依据时,尤其是在ping命令返回良好结果时,你可能不应该这样做。可以参考ping命令的返回值:如果至少从指定主机收到一个响应,则ping实用程序返回退出状态为零;如果传输成功但未收到响应,则返回状态为二;如果发生错误,则从<sysexits.h>返回另一个值。换句话说,建议使用如下命令:
((count = 60))                           # Maximum number to try.
while [[ $count -ne 0 ]] ; do
    ping -c 1 8.8.8.8                    # Try once.
    rc=$?
    if [[ $rc -eq 0 ]] ; then
        ((count = 1))                    # If okay, flag loop exit.
    else
        sleep 1                          # Minimise network storm.
    fi
    ((count = count - 1))                # So we don't go forever.
done

if [[ $rc -eq 0 ]] ; then                # Make final determination.
    echo `say The internet is back up.`
else
    echo `say Timeout.`
fi

2
计数=1行应该是((count = 1))以处理空格。 - MSumulong
2
@MSumulong,没错,很难相信我(以及其他人)在过去的三年里都忽略了这个问题。已经修复,并且将所有对count的赋值都保持一致。 - paxdiablo
使用非管理员用户使用ping循环而不等待/休眠会触发洪水预防机制,导致结果不一致。如果您真的打算淹没网络,则可能需要添加“-A”。 - Payam

14
您不需要使用echo或grep。您可以这样做:
ping -oc 100000 8.8.8.8 > /dev/null && say "up" || say "down"

9
这个对我有用:ping -c 2 8.8.8.8 > /dev/null && echo "up" || echo "down"。 - Katie
2
标志“-c 100000”表示尝试“100000”次。如果没有这个标志,它将无限重试(您需要中断它)。但是由于“-o”,它将在第一次成功的ping时返回。 - luckydonald
2
这仅适用于OSX。在Windows或Linux上,Ping没有-o标志。 - Dumle29
是的,原始问题和这个答案都假定使用 macOS。请注意,重点是使用 Mac 的 say 命令报告成功的 ping。在此答案下的第一个评论中,您将看到在 Linux 上使用 psecho 的变体。 - Rob Davis
Linux Ubuntu 18.04的ping命令没有-o选项。有没有相应的替代方法? - Gabriel Staples
我刚刚在这里问了一个问题:Unix & Linux: Equivalent of ping -o on Linux - Gabriel Staples

12

这也可以使用超时来完成:

# Ping until timeout or 1 successful packet
ping -w (timeout) -c 1

正是我所寻找的。 - Lyuboslav
奇怪。它显示了“-bash:语法错误,附近的标记`('” - ー PupSoZeyDe ー
1
@pupsozeyde 这是因为您不应该包括括号,应该像这样使用 ping -w 30 -c 1 来设置 30 秒的超时时间。 - Charly

4
如果您使用-o选项,BSD ping(也适用于macOS)将在接收一个回复数据包后退出。
更多阅读:https://www.freebsd.org/cgi/man.cgi?query=ping 编辑paxdiablo makes a very good point关于利用ping的退出状态来获取优势。我会这样做:
#!/usr/bin/env bash
echo 'Begin ping'
if ping -oc 100000 8.8.8.8 > /dev/null; then
    echo $(say 'timeout')
else
    echo $(say 'the Internet is back up')
fi

ping 命令将发送最多 100,000 个数据包,然后以失败状态退出——除非它收到一个回复数据包,在这种情况下,它将以成功状态退出。然后 if 将执行相应的语句。

编辑 2:Chris 指出我的逻辑是相反的,这真是令人尴尬。此外,echo 命令(从问题继承而来)没有任何作用,也没有必要专门要求使用 bash。我对 12 年前的自己感到失望。今天我会写成:

#!/bin/sh
if ping -oc 100000 8.8.8.8 >/dev/null; then
    say 'the Internet is back up'
else
    say timeout
fi

1
Linux Ubuntu 18.04的ping命令没有-o选项。有没有相应的替代选项? - Gabriel Staples
2
你的代码写反了。当有连接时(ping 返回 true),它会显示“超时”,而当没有连接时(ping 返回 false),它会显示“网络已恢复”。 - Chris
@Chris 谢谢,你说得对。我完全不知道12年前自己在想什么(!)。我最好的猜测是忽视了从原始代码片段中删除grep会改变退出状态逻辑(并且没有测试)。 - Lawrence Velázquez
@Chris 谢谢,你说得对。我完全不知道12年前我在想什么(!)。我最好的猜测是忽略了从 OP 的第一个代码片段中删除 grep 会翻转退出状态逻辑的事实(并没有费心测试)。 - Lawrence Velázquez

3

我在Mac OS X上使用这个Bash脚本每分钟测试一次互联网状态。

#address=192.168.1.99  # forced bad address for testing/debugging
address=23.208.224.170 # www.cisco.com
internet=1             # default to internet is up

while true;
do
    # %a  Day of Week, textual
    # %b  Month, textual, abbreviated
    # %d  Day, numeric
    # %r  Timestamp AM/PM
    echo -n $(date +"%a, %b %d, %r") "-- " 
    ping -c 1 ${address} > /tmp/ping.$
    if [[ $? -ne 0 ]]; then
        if [[ ${internet} -eq 1 ]]; then   # edge trigger -- was up now down
            echo -n $(say "Internet down") # OSX Text-to-Speech
            echo -n "Internet DOWN"
        else
            echo -n "... still down"
        fi
        internet=0
    else
        if [[ ${internet} -eq 0 ]]; then     # edge trigger -- was down now up
            echo -n $(say "Internet back up") # OSX Text-To-Speech
        fi
        internet=1
    fi   
    cat /tmp/ping.$ | head -2 | tail -1
    sleep 60 ; # sleep 60 seconds =1 min
done

1

Here's my one-liner solution:

screen -S internet-check -d -m -- bash -c 'while ! ping -c 1 google.com; do echo -; done; echo Google responding to ping | mail -s internet-back my-email@example.com'

这段代码会在新的屏幕会话中无限制地执行ping直到有响应,随后会发送电子邮件至my-email@example.com。在手机收到邮件时非常有用。
(您可能需要通过运行echo test | mail -s test my-email@example.com来检查mail是否正确配置。当然,在done;之后您可以根据自己的想法进行任何操作,如发出提示音、打开浏览器等。)

0

我喜欢paxdiablo的脚本,但想要一个可以无限运行的版本。这个版本会一直运行ping,直到建立连接,然后打印一条消息。

echo "Testing..."

PING_CMD="ping -t 3 -c 1 google.com > /dev/null 2>&1"

eval $PING_CMD

if [[ $? -eq 0 ]]; then
    echo "Already connected."
else
    echo -n "Waiting for connection..."

    while true; do
        eval $PING_CMD

        if [[ $? -eq 0 ]]; then
            echo
            echo Connected.
            break
        else
            sleep 0.5
            echo -n .
        fi
    done
fi

我还有一个这个脚本的Gist链接,如果需要,我会更新其中的修复和改进。


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