Why is 1>a.txt 2>&1 different from 1>a.txt 2>a.txt ? (Example shown)

EDIT

Please see not only the accepted answer but also the other one(s).

Question

Why does redirecting both STDOUT and STDERR to the same file not work, although it looks like the same as 1>[FILENAME] 2>&1 ?

Here is an example:

perl -e 'print "1\n" ; warn "2\n";' 1>a.txt 2>a.txt
cat a.txt
# outputs '1' only.

Well, why? I thought that this works because... STDOUT is redirected to a.txt and so is STDERR. What happened to STDERR?


With 1>a.txt 2>&1, file descriptor #1 is duplicated to #2. They both reference the same "open file", and they both share the current position and r/w mode. (There's actually no difference at all between using 2>&1 and 2<&1.)

With 1>a.txt 2>a.txt, both file descriptors are opened independently and have separate cursor positions. (The file gets truncated twice too.) If you write "Hello" to fd #1, its position is advanced to byte 5, but fd #2 remains at byte 0. Printing to fd #2 will just overwrite the data starting from 0.

This is easy to see if the second write is shorter:

$ perl -e 'STDOUT->print("abcdefg\n"); STDOUT->flush; STDERR->print("123");' >a.txt 2>a.txt

$ cat a.txt 
123defg

Note that Perl has internal buffering, so in this example an explicit flush() is necessary to ensure that fd #1 data is written before fd #2 data. Otherwise, the streams would be flushed in unpredictable order on exit.

For comparison, if the file descriptors are shared, the writes just follow each other:

$ perl -e 'STDOUT->print("abcdefg\n"); STDOUT->flush; STDERR->print("123");' >a.txt 2>&1

$ cat a.txt 
abcdefg
123