Is this a typo in Bash manual's redirection section?
Duplication is really the important part here.
Let's see where the file descriptors are going to before redirection. This is normally the current terminal, e.g.:
STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1
Now, if we call ls -l
without redirection, output and error messages go to my terminal under /dev/pts/1
.
If we first redirect the STDOUT
to a file (ls -l > dirlist
), it looks like this:
STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1
When we then redirect STDERR
to a duplicate of STDOUT
's file descriptor (ls -l > dirlist 2>&1
), STDERR
goes to a duplicate of /home/bon/dirlist
:
STDOUT ---> /home/bon/dirlist
STDERR ---> /home/bon/dirlist
If we would first redirect STDERR
to a duplicate of STDOUT
's file descriptor (ls -l 2>&1
):
STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1
and then STDOUT
to a file (ls -l 2>&1 > dirlist
), we would get this:
STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1
Here, STDERR
is still going to the terminal.
You see, the order in the man page is correct.
Testing Redirection
Now, you can test that yourself. Using ls -l /proc/$$/fd/
, you see where STDOUT
(with fd 1) and STDERR
(with fd 2), are going for the current process:
$ ls -l /proc/$$/fd/
total 0
lrwx------ 1 bon bon 64 Jul 24 18:19 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 07:41 2 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 255 -> /dev/pts/1
Let's create a small shell script that shows where your file descriptors are pointed. This way, we always get the state when calling ls
, including any redirection from the calling shell.
$ cat > lookfd.sh
#!/bin/sh
ls -l /proc/$$/fd/
^D
$ chmod +x lookfd.sh
(With CtrlD, you send an end-of-file and so stop the cat
command reading from STDIN
.)
Now, call this script with varying combinations of redirection:
$ ./lookfd.sh
total 0
lrwx------ 1 bon bon 64 Jul 24 19:08 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:08 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out
$ cat foo.out
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh 2>&1 > foo.out
$ cat foo.out
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out 2>&1
$ cat foo.out
total 0
lrwx------ 1 bon bon 64 Jul 24 19:11 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:11 1 -> /home/bon/foo.out
l-wx------ 1 bon bon 64 Jul 24 19:11 2 -> /home/bon/foo.out
lr-x------ 1 bon bon 64 Jul 24 19:11 255 -> /home/bon/lookfd.sh
You can see, that the file descriptors 1 (for STDOUT
) and 2 (for STDERR
) vary. For fun, you could also redirect STDIN
and see the result:
$ ./lookfd.sh < /dev/zero
total 0
lr-x------ 1 bon bon 64 Jul 24 19:18 0 -> /dev/zero
lrwx------ 1 bon bon 64 Jul 24 19:18 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:18 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:18 255 -> /home/bon/lookfd.sh
(Question left to the reader: Where does file descriptor 255 point? ;-))
No, the manual is right.
If at first 1 points to the terminal, and 2 also to the terminal, then:
command 2>&1 1>somewhere
redirection evaluatino will happen from left to right.
So it will FIRST evaluate 2>&1
, and thus FIRST copy what fd 1
used to point to (ie, the file descriptor of the terminal
, usually /dev/tty) into fd 2
.
So at that point fd 2
now points to where fd 1
used to point to (the terminal
)
And THEN it evaluates the 1>somewhere
part, and thus will copy the file descriptor of somewhere
in fd 1
(so at that point, fd 1
now points to somewhere
, and fd 2
still points to the terminal
)
So it does indeed print 1 into "somewhere" and 2 into the terminal, as 2 was duplicated from 1 BEFORE 1 has been changed.
The other order:
command 1>somewhere 2>&1
will first redirect fd 1
to somewhere
, and then copy that same reference into fd 2, so at the end 2 also points to somewhere
.
But they are not "linked" from now on. Each can still be redirected separately.
ex:
command 1>somewhere 2>&1
exec 2>/dev/null
At the end of that one, fd 1
points to somewhere
, and fd 2
is directed to /dev/null
Usual names for fd 1
is STDOUT (standard output), and usual name for fd 2
is STDERR (standard error, as it's commonly used to display errors without interfering with STDOUT)