Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

While loop stops reading after the first line in Bash

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.

like image 576
bcbishop Avatar asked Dec 10 '12 11:12

bcbishop


People also ask

How do I continue a new line in bash?

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.

What does #$ mean in bash?

#$ 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.

How do you continue a loop in shell?

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.

How does while loop work in bash?

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.


2 Answers

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.

like image 137
dogbane Avatar answered Sep 16 '22 14:09

dogbane


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 
like image 22
tripleee Avatar answered Sep 18 '22 14:09

tripleee