while loop to read file ends prematurely

The eventual goal is to have my bash script execute a command on multiple servers. I almost have it set up. My SSH authentication is working, but this simple while loop is killing me. When I execute the while loop, reading my file for host names, it works fine when I run a

ssh $HOST "uname -a"

but when I attempt to run another ssh command,

ssh $HOST "oslevel -s"

the while loop ends early! I can't figure it out. Why would the while read do loop run perfectly fine with the first command, but not when the second is added?

I have a simple text file called hosts.list that has 4 hostnames, one per line.

    $ cat hosts.list
    pcced1bip04
    pcced1bit04
    pcced1bo02
    pcced1bo04

    $ cat getinfo.bash
    #!/bin/bash
    set -x
    while read HOST
      do
        echo $HOST
        ssh $HOST "uname -a"
        #ssh $HOST "oslevel -s"
        echo ""
      done  < hosts.list`

When it runs, it works fine. It goes through the file, line by line and gets the results of "uname -a". So everything is fine, right? (Sorry, but I turned on set -x).

    $ ./getinfo.bash
    + read HOST
    + echo pcced1bip04
    pcced1bip04
    + ssh pcced1bip04 'uname -a'
    AIX pcced1bip04 1 6 0001431BD400
    + echo ''

    + read HOST
    + echo pcced1bit04
    pcced1bit04
    + ssh pcced1bit04 'uname -a'
    AIX pcced1bit04 1 6 0001431BD400
    + echo ''

    + read HOST
    + echo pcced1bo02
    pcced1bo02
    + ssh pcced1bo02 'uname -a'
    AIX pcced1bo02 1 6 0009FE2AD400
    + echo ''

    + read HOST
    + echo pcced1bo04
    pcced1bo04
    + ssh pcced1bo04 'uname -a'
    AIX pcced1bo04 1 6 0009FE2AD400
    + echo ''

    + read HOST
    $

The problem occurs when I enable the line [ssh $HOST "oslevel -s"]. When I do, the script only reads the first line of the file, and then stops. Why won't it go onto the other lines?

    $ ./getinfo.bash
    + read HOST
    + echo pcced1bip04
    pcced1bip04
    + ssh pcced1bip04 'uname -a'
    AIX pcced1bip04 1 6 0001431BD400
    + ssh pcced1bip04 'oslevel -s'
    6100-06-02-1044
    + echo ''

    + read HOST
    $

If I had a problem with my script, why would it be working perfectly fine with just the [ssh $HOST "uname -a"] in the while loop?


If you run commands which read from stdin (such as ssh) inside a loop, you need to ensure that either:

  • Your loop isn't iterating over stdin
  • Your command has had its stdin redirected:

...otherwise, the command can consume input intended for the loop, causing it to end.

The former:

while read -u 5 -r hostname; do
  ssh "$hostname" ...
done 5<file

...which, using bash 4.1 or newer, can be rewritten with automatic file descriptor assignment as so:

while read -u "$file_fd" -r hostname; do
  ssh "$hostname" ...
done {file_fd}<file

The latter:

while read -r hostname; do
  ssh "$hostname" ... </dev/null
done <file

...can also, for ssh alone, can also be approximated with the -n parameter (which also redirects stdin from /dev/null):

while read -r hostname; do
  ssh -n "$hostname"
done <file

Assign to an array before the loop, so that you are not using stdin for your loop variables. The ssh inside the loop can then use stdin without interfering with your loop.

readarray a <  hosts.list
for HOST in "${a[@]}"; do 
       ssh $HOST "uname -a"
       #...other stuff in loop
done