Differences between revisions 12 and 14 (spanning 2 versions)
Revision 12 as of 2011-04-18 12:33:44
Size: 1964
Editor: GreyCat
Comment: (bash-only)
Revision 14 as of 2012-06-20 16:05:20
Size: 1862
Editor: ormaaj
Comment:
Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
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: When [[BashFAQ/001|reading a file line by line]], if a command inside the loop also reads stdin, it can exhaust the input file. For example:
Line 8: 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 25: Line 24:
    ffmpeg </dev/null -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 \
      
"${file%.avi}".mkv
    ffmpeg -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 "${file%.avi}".mkv </dev/null
Line 30: Line 28:
Notice the redirection on the ffmpeg line: `</dev/null`. See the [[BashGuide/InputAndOutput#Redirection|redirection section]] of the BashGuide for more information on this. Notice the [[BashGuide/InputAndOutput#Redirection|redirection]] on the ffmpeg line: `</dev/null`. The ssh example can be fixed the same way, or with the `-n` switch (at least with [[http://www.openssh.org/|OpenSSH]]).
Line 32: Line 30:
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:
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 FileDescriptor that a random program is less likely to read from:
Line 38: Line 34:
  done 3< file   done 3<file
Line 41: Line 37:
or use read's `-u` option (Bash only): or use read's `-u` option (Not POSIX):
Line 47: Line 43:
  done 3< file   done 3<file

I'm reading a file line by line and running ssh or ffmpeg, but everything after the first line is eaten!

When reading a file line by line, if a 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 -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 "${file%.avi}".mkv </dev/null
  done < <(find . -name '*.avi')

Notice the redirection on the ffmpeg line: </dev/null. 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 FileDescriptor that a random program is less likely to read from:

  while read <&3 line; do
    ......
  done 3<file

or use read's -u option (Not POSIX):

  # Bash
  while read -u 3 line; do
    ......
  done 3<file

BashFAQ/089 (last edited 2024-04-13 21:56:35 by Reg)