Reading STDERR from a Program (Perl Cookbook, 2nd Edition)

## 16.7. Reading STDERR from a Program

### 16.7.1. Problem

You want to run a program as you would with `system`, backticks, or `open`, but you don't want its `STDERR` to be sent to your `STDERR`. You would like to be able to either ignore or read the `STDERR`.

### 16.7.2. Solution

Use the shell's numeric redirection and duplication syntax for file descriptors. (We don't check the return value from `open` here in order to make the examples easier to read, but you should always check it in your programs!)

To capture a command's `STDERR` and `STDOUT` together:

>     $output = `cmd 2>&1`;                          # with backticks
>     # or
>     $pid = open(PH, "cmd 2>&1 |");                 # with an open pipe
>     while (<PH>) { }                               # plus a read

To capture a command's `STDOUT` and discard its `STDERR`:

>     $output = `cmd 2>/dev/null`;                   # with backticks
>     # or
>     $pid = open(PH, "cmd 2>/dev/null |");          # with an open pipe
>     while (<PH>) { }                               # plus a read

To capture a command's `STDERR` and discard its `STDOUT`:

>     $output = `cmd 2>&1 1>/dev/null`;              # with backticks
>     # or
>     $pid = open(PH, "cmd 2>&1 1>/dev/null |");     # with an open pipe
>     while (<PH>) { }                               # plus a read

To exchange a command's `STDOUT` and `STDERR`, i.e., capture the `STDERR` but have its `STDOUT` come out on our old `STDERR`:

>     $output = `cmd 3>&1 1>&2 2>&3 3>&-`;           # with backticks
>     # or
>     $pid = open(PH, "cmd 3>&1 1>&2 2>&3 3>&-|");   # with an open pipe
>     while (<PH>) { }                               # plus a read

To read both a command's `STDOUT` and its `STDERR` separately, it's easiest and safest to redirect them separately to files, and then read from those files when the program is done:

>     system("program args 1>/tmp/program.stdout 2>/tmp/program.stderr");

### 16.7.3. Discussion

When you launch a command with backticks, a piped `open`, or `system` on a single string, Perl checks for characters special to the shell. These allow you to redirect the new program's file descriptors. `STDIN` is file descriptor number 0, `STDOUT` number 1, and `STDERR` number 2. You can then use `2>`file to redirect `STDERR` to a file. The special notation `&N` where N is a file descriptor number is used to redirect to a file descriptor. Therefore, `2>&1` points `STDERR` at `STDOUT`.

Table 16-1 lists some interesting shell file descriptor redirections.

#### Table 16-1. Redirections and their meanings

Redirection | Meaning
--- | ---
`0</dev/null` | Make `STDIN` give immediate EOF
`1>/dev/null` | Discard `STDOUT`
`2>/dev/null` | Discard `STDERR`
`2>&1` | Send `STDERR` to `STDOUT` instead
`2>&-` | Close `STDERR` (not recommended)
`3<>/dev/tty` | Open fd 3 to /dev/tty in read-write mode

Using this, let's examine the most complicated of the redirection sequences from the Solution:

>     $output = `cmd 3>&1 1>&2 2>&3 3>&-`;

There are four steps here:

Step A: `3>&1`
  Make a new file descriptor, number 3, be a copy of number 1. This saves the destination of `STDOUT` in the new file descriptor we've just opened.
Step B: `1>&2`
  Make `STDOUT` go wherever `STDERR` had been going. We still have the saved destination squirreled away in descriptor 3.
Step C: `2>&3`
  Make file descriptor 2 a copy of number 3. That means that `STDERR` is now going out where `STDOUT` originally had been going.
Step D: `3>&-`
  Since we're done moving streams around, keep everything nice and tidy and close our temporary file descriptor. This avoids file descriptor leaks.

If that's confusing, it might help to think in terms of regular variables and a sequence of assignment statements, with `$fd1` representing `STDOUT` and `$fd2` representing `STDERR`. If you wanted to exchange the two variables, you'd use a temporary file to hold one value. That's all we're doing here.

>     $fd3 = $fd1;
>     $fd1 = $fd2;
>     $fd2 = $fd3;
>     $fd3 = undef;

When all's said and done, the string returned from the backticks is the command's `STDERR`, and its `STDOUT` has been diverted to the original `STDERR`.

Ordering is important in all of these examples because the shell processes file descriptor redirections in strictly left to right order.

>     system("prog args 1>tmpfile 2>&1");
>     system("prog args 2>&1 1>tmpfile");

The first command sends both standard out and standard error to the temporary file. The second command sends only the old standard output there, and the old standard error shows up on the old standard out. If that's confusing, think in terms of assignments to variables representing file descriptors. For example:

>     # system ("prog args 1>tmpfile 2>&1");
>     $fd1 = "tmpfile";        # change stdout destination first
>     $fd2 = $fd1;             # now point stderr there, too

is very different from:

>     # system("prog args 2>&1 1>tmpfile");
>     $fd2 = $fd1;             # stderr same destination as stdout
>     $fd1 = "tmpfile";        # but change stdout destination

### 16.7.4. See Also

Your system's sh(1) manpage (if you have one) for details about file descriptor redirection; the `system` function in Chapter 29 of Programming Perl and in perlfunc(1)

Copyright © 2003 O'Reilly & Associates. All rights reserved.