Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I am using ffmpeg to get the meta info of an audio clip. But I am unable to grep it.

    $ ffmpeg -i 01-Daemon.mp3  |grep -i Duration
    FFmpeg version SVN-r15261, Copyright (c) 2000-2008 Fabrice Bellard, et al.
      configuration: --prefix=/usr --bindir=/usr/bin 
      --datadir=/usr/share/ffmpeg --incdir=/usr/include/ffmpeg --libdir=/usr/lib
      --mandir=/usr/share/man --arch=i386 --extra-cflags=-O2 
      ...

I checked, this ffmpeg output is directed to stderr.

$ ffmpeg -i 01-Daemon.mp3 2> /dev/null

So I think that grep is unable to read error stream to catch matching lines. How can we enable grep to read error stream?

Using nixCraft link, I redirected standard error stream to standard output stream, then grep worked.

$ ffmpeg -i 01-Daemon.mp3 2>&1 | grep -i Duration
  Duration: 01:15:12.33, start: 0.000000, bitrate: 64 kb/s

But what if we do not want to redirect stderr to stdout?

share|improve this question
1  
I believe that grep can only operate on stdout (Although I can't find the canonical source to back that up), which means that any stream needs to be converted to stdout first. –  Stefan Lasiewski Oct 26 '10 at 18:20
4  
@Stefan: grep can only operate on stdin. It's the pipe created by the shell that connects grep's stdin to the other command's stdout. And the shell can only connect an stdout to an stdin. –  Gilles Oct 26 '10 at 19:16
    
Whoops, you're right. I think that's what I really meant to say , I just didn't think it through. Thanks @Giles. –  Stefan Lasiewski Oct 27 '10 at 17:37
    
Do you want it to still print stdout? –  Mikel Feb 8 '11 at 2:06
add comment

7 Answers

None of the usual shells (even zsh) permits pipes other than from stdout to stdin. But all Bourne-style shells support file descriptor reassignment (as in 1>&2). So you can temporarily divert stdout to fd 3 and stderr to stdout, and later put fd 3 back onto stdout. If stuff produces some output on stdout and some output on stderr, and you want to apply filter on the error output leaving the standard output untouched, you can use { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1.

$ stuff () {
  echo standard output
  echo more output
  echo standard error 1>&2
  echo more error 1>&2
}
$ filter () {
  grep a
}
$ { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1
standard output
more output
standard error
share|improve this answer
add comment

This is similar to phunehehe's "temp file trick", but uses a named pipe instead, allowing you to get results slightly closer to when they are output, which can be handy for long-running commands:

$ mkfifo mypipe
$ command 2> mypipe | grep "pattern" mypipe

In this construction, stderr will be directed to the pipe named "mypipe". Since grep has been called with a file argument, it won't look to STDIN for its input. Unfortunately, you will still have to clean up that named pipe once you are done.

If you are using Bash 4, there is a shortcut syntax for command1 2>&1 | command2, which is command1 |& command2. However, I believe that this is purely a syntax shortcut, you are still redirecting STDERR to STDOUT.

share|improve this answer
    
Related: stackoverflow.com/questions/2871233/… –  Stefan Lasiewski Oct 26 '10 at 18:00
add comment

See below for the script used in these tests.

Grep can only operate on stdin, so therefore you must convert the stderr stream in a form that Grep can parse.

Normally, stdout and stderr are both printed to your screen:

$ ./stdout-stderr.sh
./stdout-stderr.sh: Printing to stdout
./stdout-stderr.sh: Printing to stderr

To hide stdout, but still print stderr do this:

$ ./stdout-stderr.sh >/dev/null
./stdout-stderr.sh: Printing to stderr

But grep won't operate on stderr! You would expect the following command to suppress lines which contain 'err', but it does not.

$ ./stdout-stderr.sh >/dev/null |grep --invert-match err
./stdout-stderr.sh: Printing to stderr

Here's the solution.

The following Bash syntax will hide output to stdout, but will still show stderr. First we pipe stdout to /dev/null, then we convert stderr to stdout, because Unix pipes will only operate on stdout. You can still grep the text.

$ ./stdout-stderr.sh 2>&1 >/dev/null | grep err
./stdout-stderr.sh: Printing to stderr

(Note that the above command is different then ./command >/dev/null 2>&1, which is a very common command).

Here's the script used for testing. This prints one line to stdout and one line to stderr:

#!/bin/sh

# Print a message to stdout
echo "$0: Printing to stdout"
# Print a message to stderr
echo "$0: Printing to stderr" >&2

exit 0
share|improve this answer
    
If you switch the redirections around, you don't need all the braces. Just do ./stdout-stderr.sh 2>&1 >/dev/null | grep err. –  Mikel Feb 8 '11 at 1:45
    
Thanks for pointing that out @Mikel. –  Stefan Lasiewski Feb 8 '11 at 1:58
add comment

If you're using bash why not employ anonymous pipes, in essence shorthand for what phunehehe said:

ffmpeg -i 01-Daemon.mp3 2> >(grep -i Duration)

share|improve this answer
1  
+1 Nice! Bash only, but way cleaner than the alternatives. –  Mikel Feb 8 '11 at 1:43
add comment

When you pipe the output of one command to another (using |), you are only redirecting standard output. So that should explain why

ffmpeg -i 01-Daemon.mp3 | grep -i Duration

doesn't output what you wanted (it does work, though).

If you don't want to redirect error output to standard output you can redirect error output to a file, then grep it later

ffmpeg -i 01-Daemon.mp3 2> /tmp/ffmpeg-error
grep -i Duration /tmp/ffmpeg-error
share|improve this answer
    
Thanks. it does work, though, you mean it is working on your machine? Secondly, as you pointed out using pipe we can only redirect stdout. I am interested in some command or bash feature that will let me redirect stderr. (but not the temp file trick) –  Andrew-Dufresne Oct 26 '10 at 4:07
    
@Andrew I mean, the command works the way it has been designed to work. It just doesn't work the way you want it to :) –  phunehehe Oct 26 '10 at 4:25
    
I don't know of any way that can redirect error output of a command to standard input of another. Would be interesting if someone can point that out. –  phunehehe Oct 26 '10 at 4:31
add comment

Gilles and Stefan Lasiewski's answers are both good, but this way is simpler:

ffmpeg -i 01-Daemon.mp3 2>&1 >/dev/null | grep "pattern"

I am assuming you don't want ffmpeg's stdout printed.

How it works:

  • pipes first
    • ffmpeg and grep are started, with ffmpeg's stdout going to grep's stdin
  • redirections next, left to right
    • ffmpeg's stderr is set to whatever its stdout is
    • ffmpeg's stdout is set to /dev/null
share|improve this answer
add comment

try this command

  • create file with random name
  • send output to the file
  • send content of the file to pipe
  • grep

ceva --> just a variable name (english = something)

ceva=$RANDOM$RANDOM$RANDOM; ffmpeg -i qwerty_112_0_0_record.flv 2>$ceva; cat $ceva | grep Duration; rm $ceva;
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.