Differences between revisions 7 and 23 (spanning 16 versions)
Revision 7 as of 2010-05-18 20:23:08
Size: 1727
Editor: GreyCat
Comment: fix link to redirection guide
Revision 23 as of 2024-04-13 21:56:35
Size: 2170
Editor: Reg
Comment: Why ffmpeg sucks up stdin.
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:
== 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? ==
Typically you'll see this behaviour in situations like these:
== I'm reading a file line by line and running ssh or ffmpeg, only the first line gets processed! ==
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 5: Line 5:
{{{
  while IFS= read -r file; do
    ffmpeg -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 \
     
"${file%.avi}".mkv
  done < <(find . -name '*.avi')
{{{#!highlight bash
# Non-working example
while IFS= read -r file; do
  ffmpeg -i "$file" -c:v libx264 -c:a aac "${file%.avi}".mkv
done < <(find . -name '*.avi')
Line 12: Line 12:
{{{
  while read host; do
    ssh "$host" command
  done <hostslist
{{{#!highlight bash
# Non-working example
while read host; do
  ssh "$host" some command
done < hostslist
Line 18: Line 19:
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. 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. However, `ffmpeg` additionally uses standard input to detect quit commands indicated by user input of `q`, thus sucking up all the input from the `find` command and starving the loop.
Line 20: Line 21:
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')
Use the `-nostdin` global option in `ffmpeg` to disable interaction on standard input:
{{{#!highlight bash
while IFS= read -r file; do
  ffmpeg -nostdin -i "$file" -c:v libx264 -c:a aac "${file%.avi}".mkv
done < <(find . -name '*.avi')
}}}
Alternatively you could use [[BashGuide/InputAndOutput#Redirection|redirection]] at the end of 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]]).

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:
{{{#!highlight bash
while IFS= read -r line <&3; do
  ...
done 3< file
Line 28: Line 36:
Notice the redirection on the ffmpeg line: `</dev/null`. See the [[BashGuide/InputAndOutput#Redirection|redirection section]] of the BashGuide for more information on this. In bash, the `read` builtin can also be told to read directly from an fd (`-u fd`) without redirection, and since bash 4.1, an available fd can be assigned (`{var}<file`) instead of hard coding a file descriptor.
Line 30: Line 38:
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
{{{#!highlight bash
# bash 4.1+
while IFS= read -r -u "$fd" line; do
  ...
done {fd}< file
exec {fd}<&-

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:

   1 # Non-working example
   2 while IFS= read -r file; do
   3   ffmpeg -i "$file" -c:v libx264 -c:a aac "${file%.avi}".mkv
   4 done < <(find . -name '*.avi')

   1 # Non-working example
   2 while read host; do
   3   ssh "$host" some command
   4 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. However, ffmpeg additionally uses standard input to detect quit commands indicated by user input of q, thus sucking up all the input from the find command and starving the loop.

Use the -nostdin global option in ffmpeg to disable interaction on standard input:

   1 while IFS= read -r file; do
   2   ffmpeg -nostdin -i "$file" -c:v libx264 -c:a aac "${file%.avi}".mkv
   3 done < <(find . -name '*.avi')

Alternatively you could use redirection at the end of 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:

   1 while IFS= read -r line <&3; do
   2   ...
   3 done 3< file

In bash, the read builtin can also be told to read directly from an fd (-u fd) without redirection, and since bash 4.1, an available fd can be assigned ({var}<file) instead of hard coding a file descriptor.

   1 # bash 4.1+
   2 while IFS= read -r -u "$fd" line; do
   3   ...
   4 done {fd}< file
   5 exec {fd}<&-

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