$ cat x
cat: x: No such file or directory
$ cat y
This is y.
$ cat x y 1> hold 2>&1
cat: x: No such file or directory
This is y.
Why stder got redirected to hold as well? stder got declared as stdout after redirecting stdout into hold and there is no more redirect after declaring happened.
TL;DR
Answer
The core reason why is because
1
no longer refers to stdout when2>&1
is read, but tohold
file, since redirections are processed left to right.First of all, remember that all commands within Unix environment have standard streams, which are referenced via file descriptors 0 for stdin, 1 for stdout, and 2 for stderr. There are of course rare exceptions, but 99% of times this is the standard file descriptors.
Redirections such as
m>n
,m>&1
andm<n
perform syscalldup2()
which makes copies of file descriptors ( aka file handles). Inm>n
,m
is typically a file descriptor, andn
can be either a file or another file descriptor. That's exactly what2>&1
are - integer references to file descriptors corresponding to stdin and stdout.When
cat x y 1> hold 2>&1
occurs, shell first will openhold
file, and reference it via next available file descriptor, typically3
, and then perform copy of that file descriptor viadup2(3,1)
.dup2()
syscall is kinda likecp
command, where you havecp old copy
. Thus, now file descriptor1
refers to the same open file description (akastruct file
in Linux kernel) independently of the other file descriptor 3.When
2>&1
is seen, shell performs seconddup2(1,2)
. So now file descriptor 2 is an independent copy of file descriptor 1, but what was it before2>&1
was seen ?1
was already pointing to the open filehold
. From there shell will performfork
andexecve
syscall to actually runcat
as subprocess, which will inherit open file descriptors.But as far as commands are concerned, in this case
cat
, it writes to file descriptors 1 and 2 without being aware they are copies of something else.You can see all of that in action with
strace
command:Side note:
If the original intention is to let
stderr
show up on screen, then2>&1
can be removed from the command.cat x y > hold
is sufficient to send stdout tohold
file and stderr to screen.If the intend is to send
stderr
viastdin
to a pipe, we will need to swap file descriptorsThis basically performs a swap like so: