1196
Comment:
|
1845
|
Deletions are marked like this. | Additions are marked like this. |
Line 1: | Line 1: |
[[Anchor(faq89)]] == I'm using a loop which runs once per line of input but it only seems to run once; everything after the first line is ignored? == |
<<Anchor(faq89)>> == I'm reading a file line by line and running ssh or ffmpeg, but everything after the first line is ignored! == When [[BashFAQ/001|reading a file line by line]], if the command inside the loop also reads stdin, it can exhaust the input file. For example: |
Line 4: | Line 5: |
Typically you'll see this behaviour in situations like these: | |
Line 6: | Line 6: |
# Non-working example | |
Line 7: | Line 8: |
ffmpeg -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 "${file%.avi}".mkv | ffmpeg -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 \ "${file%.avi}".mkv |
Line 9: | Line 11: |
}}} | |
Line 10: | Line 13: |
while read host do ssh "$host" command |
{{{ # Non-working example while read host; do ssh "$host" some command |
Line 16: | Line 20: |
What's happening here? Let's take the first example. `read` reads a line from standard input (FD 0), puts it in the file parameter, and then ffmpeg is executed. Like any program you execute from BASH, ffmpeg inherits standard input, which for some reason it reads. I don't know why. Here's how you make it work: | What's happening here? Let's take the first example. `read` reads a line from standard input (FD 0), puts it in the file parameter, and then `ffmpeg` is executed. Like any program you execute from BASH, `ffmpeg` inherits standard input, which for some reason it reads. I don't know why. But in any case, when `ffmpeg` reads stdin, it sucks up all the input from the `find` command, starving the loop. Here's how you make it work: |
Line 19: | Line 25: |
ffmpeg </dev/null -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 "${file%.avi}".mkv | ffmpeg </dev/null -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 \ "${file%.avi}".mkv |
Line 23: | Line 30: |
Notice the redirection on the ffmpeg line: `</dev/null`. See the [:BashGuide#Redirection:redirection section] of the BASH Guide for more information on this. | Notice the redirection on the ffmpeg line: `</dev/null`. See the [[BashGuide/InputAndOutput#Redirection|redirection section]] of the BashGuide for more information on this. |
Line 25: | Line 32: |
The ssh example can be fixed the same way, or with the `-n` switch (at least with `openssh`). | The ssh example can be fixed the same way, or with the `-n` switch (at least with [[http://www.openssh.org/|OpenSSH]]). Sometimes with large loops it might be difficult to work out what's reading from stdin; or a program might change its behaviour when you add `</dev/null` to it. In this case you can make read use a different file descriptor that a random program is less likely to read from: {{{ while read <&3 line; do ...... done 3<file }}} |
I'm reading a file line by line and running ssh or ffmpeg, but everything after the first line is ignored!
When reading a file line by line, if the command inside the loop also reads stdin, it can exhaust the input file. For example:
# Non-working example while IFS= read -r file; do ffmpeg -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 \ "${file%.avi}".mkv done < <(find . -name '*.avi')
# Non-working example while read host; do ssh "$host" some command done <hostslist
What's happening here? Let's take the first example. read reads a line from standard input (FD 0), puts it in the file parameter, and then ffmpeg is executed. Like any program you execute from BASH, ffmpeg inherits standard input, which for some reason it reads. I don't know why. But in any case, when ffmpeg reads stdin, it sucks up all the input from the find command, starving the loop.
Here's how you make it work:
while IFS= read -r file; do ffmpeg </dev/null -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 \ "${file%.avi}".mkv done < <(find . -name '*.avi')
Notice the redirection on the ffmpeg line: </dev/null. See the redirection section of the BashGuide for more information on this.
The ssh example can be fixed the same way, or with the -n switch (at least with OpenSSH).
Sometimes with large loops it might be difficult to work out what's reading from stdin; or a program might change its behaviour when you add </dev/null to it. In this case you can make read use a different file descriptor that a random program is less likely to read from:
while read <&3 line; do ...... done 3<file