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