I have the following shell script. The purpose is to loop thru each line of the target file (whose path is the input parameter to the script) and do work against each line. Now, it seems only work with the very first line in the target file and stops after that line got processed. Is there anything wrong with my script?
#!/bin/bash # SCRIPT: do.sh # PURPOSE: loop thru the targets FILENAME=$1 count=0 echo "proceed with $FILENAME" while read LINE; do let count++ echo "$count $LINE" sh ./do_work.sh $LINE done < $FILENAME echo "\ntotal $count targets"
In do_work.sh
, I run a couple of ssh
commands.
From the bash manual: The backslash character '\' may be used to remove any special meaning for the next character read and for line continuation. thanks.
#$ does "nothing", as # is starting comment and everything behind it on the same line is ignored (with the notable exception of the "shebang"). $# prints the number of arguments passed to a shell script (like $* prints all arguments). Follow this answer to receive notifications. edited Jul 9 at 13:55.
continue skips to the next iteration of an enclosing for, select, until, or while loop in a shell script. If a number n is given, execution continues at the loop control of the nth enclosing loop.
The while loop is used to performs a given set of commands an unknown number of times as long as the given condition evaluates to true. The while statement starts with the while keyword, followed by the conditional expression. The condition is evaluated before executing the commands.
The problem is that do_work.sh
runs ssh
commands and by default ssh
reads from stdin which is your input file. As a result, you only see the first line processed, because the command consumes the rest of the file and your while loop terminates.
This happens not just for ssh
, but for any command that reads stdin, including mplayer
, ffmpeg
, HandBrakeCLI
, and more.
To prevent this, pass the -n
option to your ssh
command to make it read from /dev/null
instead of stdin. Other commands have similar flags, or you can universally use < /dev/null
.
More generally, a workaround which isn't specific to ssh
is to redirect standard input for any command which might otherwise consume the while
loop's input.
while read -r LINE; do let count++ echo "$count $LINE" sh ./do_work.sh "$LINE" </dev/null done < "$FILENAME"
The addition of </dev/null
is the crucial point here (though the corrected quoting is also somewhat important; see also When to wrap quotes around a shell variable?). You will want to use read -r
unless you specifically require the legacy slightly odd behavior you get without -r
.
Another workaround of sorts which is somewhat specific to ssh
is to make sure any ssh
command has its standard input tied up, e.g. by changing
ssh otherhost some commands here
to instead read the commands from a here document, which conveniently (for this particular scenario) ties up the standard input of ssh
for the commands:
ssh otherhost <<'____HERE' some commands here ____HERE
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With