15

I know what this command does:

command 1>/dev/null 2>&1

But what, if anything, does the following do?

command 2>&1 1>/dev/null

I still see standard error output with the second command, so it doesn't do what I expected it to do at least...

This is reproduced on both Windows/cmd.exe and Linux/Bash (I was about to ask this question on Unix & Linux instead of Super User, but I noticed that it can be reproduced on Windows cmd.exe too, so I guess it fits better on Super User than Unix & Linux then?)

2
  • 8
    I describe 2>&1 as "redirected fd2 to whatever fd1 is currently using" -- subsequently redirecting fd1 does not affect what fd2 is using. Oct 25 at 13:54
  • Great question.
    – Nifle
    Oct 28 at 20:01
26

A further example might be helpful: let's redirect fd1 to a file, redirect fd2 to fd1, then redirect fd1 to a different file:

$ ( echo "this is stdout"; echo "this is stderr" >&2 ) 1>foo 2>&1 1>bar
$ cat foo
this is stderr
$ cat bar
this is stdout

We can see that 2>&1 sends stderr to the "foo" file that stdout was redirected to, but when we redirect stdout to "bar" we don't alter stderr's destination.

Similarly, 2>&1 1>/dev/null redirects stderr to whatever stdout is pointing to (see /proc/$$/fd), and when stdout is discarded, stderr is not altered (still visible).

This is the technique used to capture a command's error output, ignoring the regular output:

error_output=$( some_command  2>&1 1>/dev/null )
12

From man bash:

"Note that the order of redirections is significant. For example, the command

ls > dirlist 2>&1

directs both standard output and standard error to the file dirlist, while the command

ls 2>&1 > dirlist

directs only the standard output to file dirlist, because the standard error was duplicated from the standard output before the standard output was redirected to dirlist."

7

It is a problem with the ordering of operations. You expect 2>&1 to redirect to 1, but instead it is redirecting to the current value of 1 instead. It feels a little like a "race condition" but isn't really, you expect both operations to happen at the same time, but they are evaluated one after the other and the value at that specific moment is what matters, not the end value.

By default 1 is pointing at your default console output stdout so doing 2>&1 at that point is redirecting stderr to your standard console output stdout.

By doing 2>&1 after redirecting 1 to another file you are redirecting it to the new file pointer that 1 points to. (/dev/null)

Essentially the redirection operator copies the pointer from where you are redirecting to rather than pointing to the other descriptor. As such the order of redirections is important.

5
  • I too would not call this a race condition but I don't know that i would have a better word to describe what Mokubai is describing. Your name makes me hungry for Hawaiian breakfast.. "I would like the Mokubai Locobai please! with extra gravy!.. Oct 25 at 13:46
  • @DanielB hopefully clarified.
    – Mokubai
    Oct 25 at 14:10
  • 2
    Race conditions happen when two (or more) simultaneous threads or processes (or whatever) try to do things at the same time, without synchronization, so that the result depends on external factors enough to be essentially random. This is absolutely nothing like that: the processing of redirections is quite well defined, and happens exactly in order, left to right.
    – ilkkachu
    Oct 25 at 21:27
  • 2
    @SeñorCMasMas: I would call it this: Misinterpreting imperative syntax (an instruction telling the shell to perform an operation) as declarative syntax (a configuration under which the shell should run a command). Redirection is the former, but it's easy to misread it as the latter.
    – Kevin
    Oct 26 at 0:50
  • @Kevin, I am a 30+ year c/c++ developer and my statement was really sort of rhetorical. Thank you though. Perhaps you educated someone with your comment. :) You are clearly very smart. Oct 26 at 2:37
5

1>/dev/null 2>&1:
1 (stdout): /dev/null
2 (stderr): /dev/null

2>&1 1>/dev/null:
1 (stdout): /dev/null
2 (stderr): original destination of 1 (stdout)


The order is important because they are processed left-to-right, using the current values of the descriptors at that point.

Let's look at 1>/dev/null 2>&1 with two different starting conditions:

            Original           After 1>/dev/null  After 2>&1
            -----------------  -----------------  -----------------
1 (stdout): /dev/tty           /dev/null          /dev/null
2 (stderr): /dev/tty           /dev/tty           /dev/null
            Original           After 1>/dev/null  After 2>&1
            -----------------  -----------------  -----------------
1 (stdout): /tmp/stdout        /dev/null          /dev/null
2 (stderr): /tmp/stderr        /tmp/stderr        /dev/null

Now, let's look at 2>&1 1>/dev/null with the same starting conditions.

            Original           After 2>&1         After 1>/dev/null
            -----------------  -----------------  -----------------
1 (stdout): /dev/tty           /dev/tty           /dev/null
2 (stderr): /dev/tty           /dev/tty           /dev/tty
            Original           After 2>&1         After 1>/dev/null
            -----------------  -----------------  -----------------
1 (stdout): /tmp/stdout        /tmp/stdout        /dev/null
2 (stderr): /tmp/stderr        /tmp/stdout        /tmp/stdout

Demo

a:

#!/usr/bin/perl
print STDOUT "Sent to STDOUT\n";
print STDERR "Sent to STDERR\n";
$ ( exec 1>/tmp/stdout 2>/tmp/stderr; ./a )

$ cat /tmp/stdout; echo .
Sent to STDOUT
.

$ cat /tmp/stderr; echo .
Sent to STDERR
.
$ ( exec 1>/tmp/stdout 2>/tmp/stderr; ./a 2>&1 1>/dev/null )

$ cat /tmp/stdout; echo .
Sent to STDERR
.

$ cat /tmp/stderr; echo .
.
$ ( exec 1>/tmp/stdout 2>/tmp/stderr; ./a 1>/dev/null 2>&1 )

$ cat /tmp/stdout; echo .
.

$ cat /tmp/stderr; echo .
.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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