nowox
nowox

Reputation: 29066

Basic stderr stdout redirection in bash

I would like to know how to properly understand the following:

In my mind I first redirect STDOUT to file, then, I redirect STDERR to STDOUT. However this seems to do the opposite:

$ perl -e "print STDERR 'stderr'; print STDOUT 'stdout'" >file 2>&1
$ cat file
stderrstdout

Now, I was supposing that I first redirect STDERR to STDOUT, so both STDOUT and STDERR are connected to STDOUT then I redirect STDOUT to file.

$ perl -e "print STDERR 'stderr'; print STDOUT 'stdout'" 2>&1 >file
stderr
$ cat file
stdout

It seems I misunderstood how I should read this. Could someone explain it to me?

Upvotes: 1

Views: 94

Answers (2)

bishop
bishop

Reputation: 39354

In your first example, bash sets up the redirection before perl is invoked. The redirection is arranged as you described: standard out goes to a file, and standard error goes to the same file. Then perl runs, and outputs first to error and then to out. Because error and out point to the same file, the file fills up in the order printed.

In your second example, the 2>&1 redirects standard error to where standard out by default goes (the console). Then you close standard out default and redirect to a file (via >file). The important point here is that when you close a redirect, all aliased redirects also close and revert to their previous locations. In this case, standard error reverts back to console while standard out goes to the new file.

I'm using language to describe the visible effect, not talking about how bash implements this under the hood.

Upvotes: 0

that other guy
that other guy

Reputation: 123400

2>&1 doesn't point stderr to stdout. It does not in any way merge the two streams into one. There is no aliasing or reverting.

Instead, 2>&1 points stderr to where stdout is currently pointed, and not to stdout itself. Afterwards, you're therefore free to change where stdout is pointing without affecting stderr.

You can think of > file 2>&1 like this:

stdout=terminal stderr=terminal  # inherited default
stdout=file                      # > file
stderr=$stdout                   # 2>&1
echo "stdout=$stdout"            # result: stdout=file
echo "stderr=$stderr"            # result: stderr=file

So both the outputs got to the file, like you're seeing.

Similarly, 2>&1 > file would be:

stdout=terminal stderr=terminal  # inherited default
stderr=$stdout                   # 2>&1
stdout=file                      # > file
echo "stdout=$stdout"            # result: stdout=file
echo "stderr=$stderr"            # result: stderr=terminal

So stderr goes to the terminal, again like you're seeing.

The actual implementation is basically just like this, but with dup2() instead of assigning to fd, and open() instead of assigning to file

Upvotes: 1

Related Questions