SSH eats stdin of while loop
Bash script is helpful if you want to do something automatically, especially in batch mode. Recently I want to upgrade a package on several hosts so I write a small script within it there is a while loop to read hosts from a file and ssh to every one and run some commands. However, the weird thing is that commands only done on the first host apparently and the while loop broke.
That did surprised me a little, see below simple scripts
#!/bin/bash
echo -e "host1\nhost2\nhost3" |
while read host; do
echo "ssh to $host"
ssh $host "echo hello"
done
The output as I expected was
ssh to host1
hello
ssh to host2
hello
ssh to host3
hello
However, the out was
ssh to host1
hello
Apparently something gone wrong, as I expected if comment the line ssh $host
“echo hello”, it works fine and print three *ssh to
Though google is not always here (in China) but it’s helpful and I found answer here and there.
So here comes the short answer, ssh eat the stdin of the while loop, so host2\nhost3\n was never sent to the while loop but eaten by ssh, we can verify that like below.
#!/bin/bash
echo -e "host1\nhost2\nhost3" |
while read host; do
echo "ssh to $host"
ssh $host "cat"
done
We’ll get the output like
ssh to host1
host2
host3
A simple solution is use ssh with -n option to redirect its stdin from /dev/null like below.
#!/bin/bash
echo -e "host1\nhost2\nhost3" |
while read host; do
echo "ssh to $host"
ssh -n $host "echo hello"
done
It will works as we expected.
ssh to host1
hello
ssh to host2
hello
ssh to host3
hello