IO Redirection - Swapping stdout and stderr
% (sh myscript.sh 3>&2 2>&1 1>&3) 2>/dev/nullI'm stderr% (sh myscript.sh 3>&2 2>&1 1>&3) >/dev/null I'm stdout
Explanation of 3>&2 2>&1 1>&3
:
3>&2
means make a copy of file descriptor 2 (fd 2) (stderr), named fd 3 (file descriptor 3). It copies the file descriptor, it doesn't duplicate the stream astee
does.2>&1
means that fd 2 ofsh myscript.sh
becomes a copy of it's fd 1 (stdout). Now, when myscript writes to it's stderr (it's fd 2), we receive it on stdout (our fd 1).1>&3
means that fd 1 ofsh myscript.sh
becomes a copy of fd 3 (stderr). Now, when myscript writes to it's stdout (it's fd 1), we receive it on stderr (our fd 2).
For sake of completeness, based on a comment by @200_success above, it is probably better to move the file descriptor 3 using 1>&3-
:
$ (sh myscript.sh 3>&2 2>&1 1>&3-) 2>/dev/nullI'm stderr$ (sh myscript.sh 3>&2 2>&1 1>&3-) >/dev/null I'm stdout
Instead of swapping file descriptors on a per-process basis, using exec
you can swap stdin & stderr for all following commands launched by the current shell :
$ (exec 3>&2 2>&1 1>&3- ; sh myscript.sh ; sh myscript.sh ) 2>/dev/nullI'm stderrI'm stderr$ (exec 3>&2 2>&1 1>&3- ; sh myscript.sh ; sh myscript.sh ) >/dev/null I'm stdoutI'm stdout
The bash hackers wiki can be very useful in this kind of things.There's a way of doing it which is not mentioned among these answers, soI'll put my two cents.
The semantics of >&N
, for numeric N, means redirect to the target ofthe file descriptor N. The word target is important since the descriptor can change target later, but once we copied that target we don't care. That's the reason why the order in which we declare of redirection is relevant.
So you can do it as follows:
./myscript.sh 2>&1 >/dev/null
That means:
redirect stderr to stdout's target, that is the stdout output stream. Nowstderr copied stdout's target
change stdout to /dev/null. This won't affect stderr, since it "copied"the target before we changed it.
No need for a third file descriptor.
It is interesting how I can't simply do >&-
, instead of >/dev/null
. This actually closes stdout, so I'm getting an error (on stderr's target, that is the actual stdout, of course :D)
line 3: echo: write error: Bad file descriptor
You can see that order is relevant by trying to swap the redirections:
./myscript.sh >/dev/null 2>&1
This will not work, because:
- We set the target of stdout to
/dev/null
- We set the target of stderr to stdout's target, that is
/dev/null
again.