ssh is reading the rest of your standard input.
while read HOST ; do … ; done < servers.txt
read reads from stdin. The
< redirects stdin from a file.
Unfortunately, the command you’re trying to run also reads stdin, so it winds up eating the rest of your file. You can see it clearly with:
$ while read HOST ; do echo start $HOST end; cat; done < servers.txt start server1.mydomain.com end server2.mydomain.com server3.mydomain.com
cat ate (and echoed) the remaining two lines. (Had read done it as expected, each line would have the “start” and “end” around the host.)
for line doesn’t redirect to stdin. (In fact, it reads the entire contents of the
servers.txt file into memory before the first iteration). So
ssh continues to read its stdin from the terminal (or possibly nothing, depending on how your script is called).
At least in bash, you can have
read use a different file descriptor.
while read -u10 HOST ; do ssh $HOST "uname -a" ; done 10< servers.txt # ^^^^ ^^
ought to work.
10 is just an arbitrary file number I picked. 0, 1, and 2 have defined meanings, and typically opening files will start from the first available number (so 3 is next to be used). 10 is thus high enough to stay out of the way, but low enough to be under the limit in some shells. Plus its a nice round number…
Alternative Solution 1: -n
As McNisse points out in his/her answer, the OpenSSH client has an
-n option that’ll prevent it from reading stdin. This works well in the particular case of
ssh, but of course other commands may lack this—the other solutions work regardless of which command is eating your stdin.
Alternative Solution 2: second redirect
You can apparently (as in, I tried it, it works in my version of Bash at least…) do a second redirect, which looks something like this:
while read HOST ; do ssh $HOST "uname -a" < /dev/null; done < servers.txt
You can use this with any command, but it’ll be difficult if you actually want terminal input going to the command.
As derobert describes
ssh reads your
To change this behavior you can add -n no ssh to prevent it from reading stdin.
ssh -n $HOST "uname -a"
You’d probably actually be better off using pssh from the parallel-ssh project.
pssh -h $hostfile -t $timeout -i $commands
-i means interactive. pssh also comes with a parallel scp and parallel rsync. What’s nice is that it runs asynchronously and will run as many threads as you ask it to. The default (non -i/interactive) is to output to separate directories for stdout/stderr, which it does by $outputdir/$hostname.