Differences between revisions 15 and 16
Revision 15 as of 2013-05-17 20:58:31
Size: 1852
Editor: GreyCat
Comment: reword question a bit
Revision 16 as of 2015-01-07 14:51:17
Size: 1705
Editor: GreyCat
Comment: read -u 3 is dumb. don't even bother showing it.
Deletions are marked like this. Additions are marked like this.
Line 6: Line 6:
  # 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 IFS= read -r file; do
  ffmpeg -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 "${file%.avi}".mkv
done < <(find . -name '*.avi')
Line 13: Line 13:
  # Non-working example
  while read host; do
   ssh "$host" some command
  done <hostslist
# Non-working example
while read host; do
  ssh "$host" some command
done <hostslist
Line 21: Line 21:
Here's how you make it work: Here's one way to make it work:
Line 23: Line 23:
  while IFS= read -r file; do
   ffmpeg -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 "${file%.avi}".mkv </dev/null
  done < <(find . -name '*.avi')
while IFS= read -r file; do
  ffmpeg -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 "${file%.avi}".mkv </dev/null
done < <(find . -name '*.avi')
Line 30: Line 30:
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: 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 32: Line 32:
  while read <&3 line; do
    ......
  done 3<file
while read <&3 line; do
  ...
done 3<file
Line 36: Line 36:

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

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

I'm reading a file line by line and running ssh or ffmpeg, only the first line gets processed!

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 one way to 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

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