如何在脚本中使用ping

13

我想要一个bash脚本,能够执行以下操作:

for c in computers:
do
   ping $c
   if ping is sucessfull:
      ssh $c 'check something'
done

如果我只使用 ssh 命令,而电脑无法响应,超时时间会非常长。因此,我想使用 ping 命令的输出来确定计算机是否存活。如何做到这一点?其他想法也很好。

9个回答

19

使用 ping 的返回值:

for C in computers; do
  ping -q -c 1 $C && ssh $C 'check something'
done

ping 如果单个 ping (-c 1) 成功,则以值 0 退出。在 ping 超时或者 $C 无法解析的情况下,它将以非零值退出。


1
这个答案是错误的,如果您没有为您正在ping的目标IP设置默认路由,您仍将得到0返回值。 - Space Rocker
3
很有趣!我刚刚查了一下 man ping,倾向于认为这种行为是 ping 中的一个 bug,因为在这种情况下我期望返回值是 2。引用一下文档中的内容:"如果 ping 没有接收到任何响应数据包,它将以代码1退出。如果指定了数据包计数和截止时间,并且在截止时间到达之前接收到少于计数数据包,则它也将以代码1退出。在其他错误时,它会以代码2退出。否则它以代码0退出,这使得使用退出代码来查看主机是否处于活动状态成为可能。" 你对此有什么看法? - Stephan202

11

ping命令中使用-w开关(或在FreeBSD和OS X上使用-t),然后检查命令的返回值。

ping -w 1 $c
RETVAL=$?
if [ $RETVAL -eq 0 ]; then
    ssh $c 'check something'
fi

你可能需要调整使用-w传递的参数,如果你要连接的主机距离较远且延迟较高。
man ping中得知:
   -w deadline
          Specify  a  timeout, in seconds, before ping exits regardless of
          how many packets have been sent or received. In this  case  ping
          does  not  stop after count packet are sent, it waits either for
          deadline expire or until count probes are answered or  for  some
          error notification from network.

6

并非所有的网络环境都允许ping命令(虽然很多可以),也不是所有的主机都会回应ping请求。我建议不要使用ping,而是设置ssh连接超时时间:

for c in computers; do
  ssh -o ConnectTimeout=2 $c '检查某些东西'
done

这是唯一正确的解决方案。当SSH连接成功时,ping失败的原因还有很多其他因素。 - Lie Ryan

1

我在1997年编写了这样的脚本,并在接下来的几年中大量使用它:sshall

它很简单,不太灵活。另一方面,它支持一些您可能不需要的检查。

一旦我开始以更多样化的方式使用ssh,我就停止使用或更新此脚本;现在我要么直接编写shell循环,要么使用Ansible adhoc commands

这个脚本:

#!/bin/sh
#
# $Id: sshall 1259 2017-06-26 16:59:42Z rp $

# sshall: ssh to multiple hosts, *last* arg is command
# with -i, also accepts input ... I'd rather dup stdin or so, but how?

PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/usr/etc; export PATH
tmpfile=/tmp/sshall-$$

# error handling
trap 'rm -f $tmpfile; exit' 1 2 3 4 13 15

#--- cmdline parsing ---#
#

Puke()
{
  if [ -n "$*" ]; then echo Fatal error: $* 1>&2; fi
  cat <<ZZ 1>&2

Usage:
  $0 [-v] [-i] [-e] [-b] [-u user] [-H] [-Y] [-P] host1 [host2 [...]] "command"

  to issue "ssh host command" for every host

  use -i flag to supply input, -e to redirect stderr to stdout,
    -v for progress messages, -b to start in the background,
    -u user to connect as the given user,
    -H to check the hostnames with 'host',
    -Y to check them with 'ypmatch',
    -P to check them with 'ping',
    -o text to pass the given option through to ssh

  note: the effect of -i is to call ssh without the -n flag
  take care: -b may fill up your process table if used on many hosts

ZZ

  exit 1
}

input=
hostlist=
verbose=
bg=
check_w_host=
check_w_ypmatch=
check_w_ping=
user_prefix=

while :
do
  case "$1" in
    -h|-help|\?*) Puke;;
    -b) bg=1
    if [ -n "$command" ]; then Puke "options must precede arguments"; fi;;
    -i) input=1
    if [ -n "$command" ]; then Puke "options must precede arguments"; fi;;
    -v) verbose=1
    if [ -n "$command" ]; then Puke "options must precede arguments"; fi;;
    -e) errtoout=1
    if [ -n "$command" ]; then Puke "options must precede arguments"; fi;;
    -o)
  if [ -n "$o_opt" ]; then Puke "specify only one -o option"; fi
       shift; o_opt="$1"
    if [ -n "$command" ]; then Puke "options must precede arguments"; fi;;
    -u) shift; user_prefix="$1@"
    if [ -n "$command" ]; then Puke "options must precede arguments"; fi;;
    -H) check_w_host=1
    if [ -n "$command" ]; then Puke "options must precede arguments"; fi;;
    -Y) check_w_ypmatch=1
    if [ -n "$command" ]; then Puke "options must precede arguments"; fi;;
    -P) check_w_ping=1
    if [ -n "$command" ]; then Puke "options must precede arguments"; fi;;
    -*) Puke "$1 is not a valid option" ;;
    "") break;;
    *) hostlist="$hostlist $command"; command=$1;;
  esac
  shift
done

if [ -z "$command" ]
then
  Puke "no command supplied"
fi

if [ -z "$hostlist" ]
then
  Puke "no host(s) supplied"
fi

case "$user_prefix" in
-*)
  Puke "no -u argument supplied" ;;
esac

if [ -n "$check_w_host" ]
then
  for h in $hostlist
  do
    if host 2>&1 >/dev/null
    then
      Puke "host cannot find '$h'"
    fi
  done
fi

if [ -n "$check_w_ypmatch" ]
then
  for h in $hostlist
  do
    if ypmatch hosts 2>&1 >/dev/null
    then
      Puke "ypmatch cannot find '$h'"
    fi
  done
fi


#--  OK, start doing useful things ---#
#

if [ -n "$input" ]
then
  # read input!
  cat >$tmpfile
  # we can do away with the $tmpfile, with a fork for every host ...
fi

Ssh()
{
  case "$errtoout" in
    "") ssh "$@" | sed "s/^/$h: /" ;;
    *)  ssh "$@" 2>&1 | sed "s/^/$h: /" ;;
  esac
}

Ssh_o()
{
  case "$o_opt" in
  "") Ssh "$@";;
  *)  Ssh -o "$o_opt" "$@";;
  esac
}

Ssh_w_tmp()
{
  if [ -f "$tmpfile" ]
  then
    cat $tmpfile | Ssh_o "$@"
  else
    Ssh_o -n "$@"
  fi
}

for h in $hostlist
do
  if [ -z "$check_w_ping" ] || ping $h 2 >/dev/null  # note: "2 >"
  # host is active
  then
    #if [ -z "`finger @$h 2>&1 | grep 'Connection refused$'`" ]
    # host accepts finger - very crude check to see if ssh will work
    # however, finger has been disabled since, where I live
    if true
    then
      if [ -n "$verbose" ]
      then
        echo "executing '$command' on '$h'" 1>&2
      fi

      case "$bg" in
      "")
          Ssh_w_tmp $user_prefix$h "$command" ;;
      *)
          Ssh_w_tmp $user_prefix$h "$command" & ;;
        esac
    fi
    fi
done

rm -f $tmpfile

0

这是我的技巧:

#ipaddress shell variable can be provided as an argument to the script.
while true
do
   nmap_output=$(nmap -p22 ${ipaddress})
   $(echo ${nmap_output} | grep -q open)
   grep_output=$?
   if [ "$grep_output" == 0 ] ; then
       #Device is LIVE and has SSH port open for clients to connect
       break
   else
       #[01 : bold
       #31m : red color
       #0m : undo text formatting
       echo -en "Device is \e[01;31mdead\e[0m right now .... !\r"
   fi
done
#\033[K : clear the text for the new line
#32 : green color
echo -e "\033[KDevice is \e[01;32mlive\e[0m !"
ssh user@${ipaddress}

不仅依赖于ping。为什么?
- 成功的ping并不能保证您成功访问ssh。您仍然可以在此脚本的开头添加ping测试,如果ping失败,则退出并从上面不执行任何操作。

上述bash脚本片段验证您尝试访问的设备是否为客户端(您)打开了SSH端口。需要安装nmap软件包。

我不明白为什么您要在该脚本中ssh到多台计算机。但是,我的脚本适用于ssh到一个设备,并可根据您的需求进行修改。


0

使用64作为测量工具是不合逻辑的。最好使用接收/丢失数据包的数量。

这个脚本可以工作:

RESULT="1"
PING=$(ping ADDRESS -c 1 | grep -E -o '[0-9]+ received' | cut -f1 -d' ')
if [ "$RESULT" != "$PING" ]
then
    DO SOMETHING
else
    DO SOMETHING
fi

0
承认原问题涉及Bash,这里提供一个示例,供任何想在Fish shell中实现此功能的人参考:
ping -q -c 1 bogus.local; and echo "pinging the host worked"; or echo "pinging the host didn't work"

0
 while true;
    do
        RESULT="1"
        PING=$(ping 8.8.8.8 -c 1 | grep -E -o '[0-9]+ received' | cut -f1 -d' ')
        if [ "$RESULT" != "$PING" ]
        then
            echo "FAIL"
            exit 0
        else
            echo "connection is okay.." 
        fi
    done

-1
在你的bash循环中使用这个:
RESULT="64"
PING=$(ping 127.0.0.1 -c 1 | grep 64 | awk '{print $1}')
if [ "$RESULT" != "$PING" ]
then
   #ping failed
else
   #ping successful, do ssh here
fi

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