Anchor(faq47)

How can I redirect stderr to a pipe?

A pipe can only carry stdout of a program. To pipe stderr through it, you need to redirect stderr to the same destination as stdout. Optionally you can close stdout or redirect it to /dev/null to only get stderr. Some sample code:

# Assume 'myprog' is a program that outputs both, stdout and stderr.

# version 1: redirect stderr towards the pipe while stdout survives (both come
# mixed)
myprog 2>&1 | grep ...

# version 2: redirect stderr towards the pipe without getting stdout (it's
# redirected to /dev/null)
myprog 2>&1 >/dev/null | grep ...

# version 3: redirect stderr towards the pipe while the "original" stdout gets
# closed
myprog 2>&1 >&- | grep ...

For further explanation of how redirections and pipes interact, see [#faq55 FAQ #55].

This has an obvious application with programs like dialog, which draws (using ncurses) windows onto the screen (stdout), and returns results on stderr. One way to deal with this would be to redirect stderr to a temporary file. But this is not necessary -- see [#faq40 FAQ #40] for examples of using dialog specifically!

In the examples above (as well as [#faq40 FAQ #40]), we either discarded stdout altogether, or sent it to a known device (/dev/tty for the user's terminal). One may also pipe stderr only but keep stdout intact (without a priori knowledge of where the script's output is going). This is a bit trickier.

# Redirect stderr to a pipe, keeping stdout unaffected.

exec 3>&1                       # Save current "value" of stdout.
myprog 2>&1 >&3 | grep ...      # Send stdout to FD 3.
exec 3>&-                       # Now close it for the remainder of the script.

# Thanks to http://www.tldp.org/LDP/abs/html/io-redirection.html

The same can be done without exec:

$ myfunc () { echo "I'm stdout"; echo "I'm stderr" >&2;}
$ { myfunc 2>&1 1>&3- | cat  > stderr.file 3>&- ; } 3>&1
I'm stdout
$ cat stderr.file
I'm stderr

The fd 3 is closed (1>&3- and 3>&-) so that the commands do not inherit it. You can check the difference on linux trying the following:

{ bash <<< 'lsof -a -p $$ -d1,2,3'   ;} 3>&1
{ bash <<< 'lsof -a -p $$ -d1,2,3' 3>&-  ;} 3>&1

To show a dialog one-liner:

exec 3>&1
dialog --menu Title 0 0 0 FirstItem FirstDescription 2>&1 >&3 | sed 's/First/Only/'
exec 3>&-

This will have the dialog window working properly, yet it will be the output of dialog (returned to stderr) being altered by the sed.

A similar effect can be achieved with process substitution:

# Bash only.
perl -e 'print "stdout\n"; warn "stderr\n"' 2> >(tr a-z A-Z)

This will pipe standard error through the tr command.

A redirection tutorial (with an example that redirects stdout to a pipe and stderr to another pipe):