how to use ping in a script
Asked Answered
W

9

13

I want a bash script that'll do:

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

If I only do ssh and the computer is iresponsive, it takes forever for the timeout. So I was thinking of using the output of ping to see if the computer is alive or not. How do I do that? Other ideas will be great also

Waddle answered 13/11, 2009 at 8:32 Comment(0)
I
19

Use ping's return value:

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

ping will exit with value 0 if that single ping (-c 1) succceeds. On a ping timeout, or if $C cannot be resolved, it will exit with a non-zero value.

Investiture answered 13/11, 2009 at 8:36 Comment(2)
This answer is wrong, if you have not setup default route for the destination IP which you are pinging, you would still get 0 returned value.Poulter
@SpaceRocker: Very interesting! I just checked man ping and I'm inclined to consider that behavior a bug in ping, since I'd expect a return value of 2, in that case. Quote: "If ping does not receive any reply packets at all it will exit with code 1. If a packet count and deadline are both specified, and fewer than count packets are received by the time the deadline has arrived, it will also exit with code 1. On other error it exits with code 2. Otherwise it exits with code 0. This makes it possible to use the exit code to see if a host is alive or not." What's your take on this?Investiture
S
11

Use the -w switch (or -t on FreeBSD and OS X) on the ping command, then inspect the command's return value.

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

You may want to adjust the parameter you pass with -w if the hosts you're connecting to are far away and the latency is higher.

From 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.
Sotelo answered 13/11, 2009 at 8:39 Comment(0)
P
6

Not all network environments allow ping to go through (though many do) and not all hosts will answer a ping request. I would recommend not to use ping, but instead set the connect timeout for ssh:

for c in compuers; do
  ssh -o ConnectTimeout=2 $c 'check something'
done
Puffy answered 8/8, 2013 at 21:53 Comment(1)
This is the only correct solution. There are many other reasons that ping would fail when an SSH connection would succeed.Pelson
Y
1

I wrote such a script in 1997 and used it heavily for some years: sshall.

It is simplistic and not very versatile. On the other hand, it supports some checks you probably don't need.

Once I started using ssh in more varied ways, I stopped using or updating this script; I now either write shell loops directly or use Ansible adhoc commands.

The script:

#!/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
Yoshida answered 13/11, 2009 at 8:38 Comment(0)
S
0

Using 64 value as measurement tool is not logical. It is better to use the number of received/lost packets instead.

This script would work:

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
Stamp answered 20/11, 2013 at 14:55 Comment(0)
A
0

Here's my hack:

#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}

Doesn't rely on just ping. Why ?
- Succesful ping doesn't guarantee you a successful ssh access. You could still add ping test as well, to the beginning of this script and exit if ping fails and do nothing from the above.

Above bash script snippet, verifies if the device you are trying to access has the SSH port open for clients(you) to connect to. Requires nmap package to be installed.

I don't understand why you want to ssh into multiple computers in that script. But, mine works for ssh into one device and can be modified to suit your needs.

Anselmo answered 18/3, 2015 at 0:29 Comment(0)
W
0

Acknowledging the original question referenced Bash, here's an example for anyone looking to accomplish this in Fish shell:

ping -q -c 1 bogus.local; and echo "pinging the host worked"; or echo "pinging the host didn't work"
Wenwenceslaus answered 8/11, 2018 at 21:36 Comment(0)
O
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
Officiary answered 22/9, 2020 at 22:0 Comment(0)
P
-1

Use this in your bash loop:

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
Poulter answered 22/2, 2013 at 14:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.