9

I have this script:

#!/bin/sh
SERVERLIST=hosts
ICMD='cat /etc/redhat-release'
while read SERVERNAME
do
   ssh $SERVERNAME $ICMD
done < "$SERVERLIST"

With hosts just like:

hostname
hostname
hostname

When I run the script ./run.sh it only goes to a single host and stops. Why is it not reading the entire host list?

1

2 Answers 2

18

ssh pre-reads stdin on the assumption that the remote job will want some data, and it makes for a faster start-up. So the first job eats the rest of your hosts list.

You can prevent this happening either using the ssh -n option, or by redirecting like ssh < /dev/null. Being a belt-and-braces man, I do both: ssh -n < /dev/null <other ssh options>.

If you really want the remote job to read something from the local system, you need to pipe it, or here-doc it, for each iteration. Even with for <host>, ssh will read an indeterminate amount of data from stdin for each host start-up.

I would always ping a remote host to get status before ssh to it. IIRC, ping -W has a hard timeout option, while ssh can hang in DNS or while connecting for 90+ seconds, for an unknown or dead host.

3
  • 1
    That makes sense! It explains why the original answer OP referenced (in my since-deleted incorrect assumption) works but this one doesn't. Commented Jan 20, 2021 at 15:24
  • @AaronD.Marasco The second answer in that thread has the same fault anyway -- I was not certain which was being cited. Commented Jan 20, 2021 at 15:38
  • Another trick is to use a separate file-descriptor in the 3<servers and read -u 3, though this isnt POSIX for read Commented Jan 21, 2021 at 0:05
7

You could also use pdsh for this, it allows running a command in parallel on multiple hosts.

pdsh -w ^hostlist.txt -R ssh "cat /etc/redhat-release"

^ defines a file with the list of hostnames, alternatively a comma separated list could be used:

pdsh -w host1,host2,host3,... -R ssh "cat /etc/redhat-release"

A result would look like:

pdsh, -w raspi,raspi.local,192.168.0.3,localhost,127.0.0.1 -R ssh "cat /etc/issue"

raspi: Raspbian GNU/Linux 10 \n \l
raspi: 
raspi.local: Raspbian GNU/Linux 10 \n \l
raspi.local: 
192.168.0.3: Raspbian GNU/Linux 10 \n \l
192.168.0.3: 
localhost: Debian GNU/Linux 10 \n \l
localhost: 
127.0.0.1: Debian GNU/Linux 10 \n \l
127.0.0.1: 

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .