4

I have two scripts:

foo.sh:

#!/bin/bash
echo -e "one\ntwo" |
while read line; do
    cat not-existing
    echo hello $line
done

bar.sh:

#!/bin/bash
echo -e "one\ntwo" |
while read line; do
    ssh user@machine 'cat not-existing' # Here is the only difference
    echo hello $line
done

And now I run them

$ ./foo.sh 
cat: not-existing: No such file or directory
hello one
cat: not-existing: No such file or directory
hello two

$ ./bar.sh cat: not-existing: No such file or directory hello one

The output of bar.sh is surprising to me. I would expect it to be the same for both scripts.

Why does the output of foo.sh and bar.sh differ? Is it a bug or a feature?


Note

The following works as I expect, i.e. the output of this is the same as the output of foo.sh:

#!/bin/bash
for line in `echo -e "one\ntwo"`; do
    ssh user@machine 'cat not-existing'
    echo hello $line
done

Why?

mejem
  • 100

1 Answers1

11

In bar.sh, the two is consumed by ssh. In the last example, the full output from echo is used by for before it starts looping.

To avoid having ssh gobble up your data from standard input, use ssh -n. This will hook up the standard input of ssh with /dev/null rather than with the standard input of the while loop.

This will do what you expect it to do:

#!/bin/bash
echo -e "one\ntwo" |
while read line; do
    ssh -n user@machine 'cat not-existing' # Here is the only difference
    echo hello $line
done

If you had written

#!/bin/bash
echo -e "one\ntwo" |
while read line; do
    ssh user@machine 'cat'
    echo hello $line
done

then the cat on the remote machine would have outputted two since its standard input is handed down to it from ssh which in turn got it from the loop and echo. It will print two rather than one since the first line of input already has been consumed by read.

Kusalananda
  • 333,661