How to keep script from swallowing all of stdin?

I have a script that reads from a pipe in a loop and runs an expect script and a normal shell script in the loop. Both scripts run ssh to another server to grab a piece of data. For example:

cat /tmp/file |
while read a b c d
do
   s=`expect-script server1 $b`
   c=`ssh $b normal-script`
   echo $s $c
done

Even though there are many lines in /tmp/file, the script quits after processing the first line. I suspect that the expect script is swallowing all of stdin so that when it returns, there is nothing left to read. How can I avoid this? I don't want any of the scripts I call to read from the stdin of the main script.


Solution 1:

cat /tmp/file |
while read a b c d
do
    {
        s=`expect-script server1 $b`
        c=`ssh $b normal-script`
        echo $s $c
    } < /dev/null
done

The { command... } syntax allows you to apply redirection or piping to a sequence of commands.

I'll also note that you don't need cat in your example. You could do this:

while read a b c d
do
    ...
done < /tmp/file

Solution 2:

It's actually ssh that's slurping up stdin. Just add the -n option:

    c=$( ssh -n $b normal-script )

If you don't want to do that, you can have your shell while loop read from a different file descriptor, leaving stdin untouched:

while read -u3 a b c d
do
    s=$( expect-script server1 $b )
    c=$( ssh $b normal-script )
    echo $s $c
done 3< /tmp/file

(assuming bash/ksh/zsh for the read -u option)