Reputation: 14790
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
Upvotes: 13
Views: 63271
Reputation: 8591
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
Upvotes: 1
Reputation: 294
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
Upvotes: 0
Reputation: 2016
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"
Upvotes: 0
Reputation: 1306
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.
Upvotes: 0
Reputation: 2519
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.
Upvotes: 11
Reputation: 6199
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
Upvotes: 0
Reputation: 3247
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
Upvotes: 6
Reputation: 787
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
Upvotes: -1
Reputation: 61469
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.
Upvotes: 19