December 18, 2016
Clarifying bash redirection for stdout and stderr
I sometimes forget some of the simplier things that I actually use quite frequently in the bash shell. Error redirection involving stdout and stderr seems to be one of them. Below are a few examples that explain things better than I can put in words. But first, note the following givens and assumptions:
stdout
== standard output == file descriptor 1stderr
== standard error == file descriptor 2&
indicates that what follows is a file descriptor and not a file name/not
is a non-existent directory/tmp
is an existing directory
kevin@localhost:~$ ls -ld /tmp /not
ls: cannot access '/not': No such file or directory
drwxrwxrwt. 14 root root 1.2K Dec 18 17:01 /tmp/
kevin@localhost:~$ ls -ld /tmp /not > /dev/null
ls: cannot access '/not': No such file or directory
kevin@localhost:~$ ls -ld /tmp /not 1> /dev/null
ls: cannot access '/not': No such file or directory
kevin@localhost:~$ ls -ld /tmp /not 2> /dev/null
drwxrwxrwt. 13 root root 1.2K Dec 18 16:00 /tmp/
The first statement is our base case and shows that both stdout(1)
and
stderr(2)
are screen printed when no redirection or file descriptors are
explicitly specified.
In the second statement, stdout(1)
is sent into /dev/null
, so we only see the
error message about the non-existent directory /not
.
The same is true of the third statement, which is technically the same as the second statement, except here I’ve explicitly included the file descriptor 1.
In the fourth statement, I’ve redirected stderr(2)
to /dev/null
and allowed
stdout(1)
to be printed to screen.
This all seems reasonable and easy to follow, so let’s take this a step further and confuse things a bit:
kevin@localhost:~$ ls -ld /tmp /not 2>&1
ls: cannot access '/not': No such file or directory
drwxrwxrwt. 13 root root 1.2K Dec 18 16:00 /tmp/
kevin@localhost:~$ ls -ld /tmp /not 2>&1 >/dev/null
ls: cannot access '/not': No such file or directory
kevin@localhost:~$ ls -ld /tmp /not 2>&1 1>/dev/null
ls: cannot access '/not': No such file or directory
In the first statement, stderr(2)
gets sent to stdout(1)
and both are printed
on the screen. Nobody has to go to /dev/null
yet.
In the second statement, 2>&1
gets preformed before any assignment to
/dev/null
. This means stderr(2)
is sent to wherever stdout happens to be going
– most likely the user’s screen. This redirection and screen print takes
place, and then stdout(1)
, which would be the result of ls -ld /tmp
, is
sent to /dev/null
and obviously discarded.
Confused? Look at the third statement.
Statement three is the same as the one directly above it (second statement),
but with stdout(1)
explicitly specified before redirection. As we can see,
stderr(2)
survives because it is processed first and sent directly to screen
before anything else is processed.
So the next step is to show that order matters:
kevin@localhost:~$ ls -ld /tmp /not 2>&1 >/dev/null
ls: cannot access '/not': No such file or directory
kevin@localhost:~$ ls -ld /tmp /not >/dev/null 2>&1
In the first statement, like I demonstrated in the set of statements earlier,
the contents of stderr(2)
gets sent off for immediate rendering on the
screen, but then, in the next step, the contents of stdout(1)
is shipped
off to /dev/null
.
In the second statement, I’ve reversed the order, so stdout(1)
gets sent to
/dev/null
. Then 2>&1
is preformed and stderr(2)
gets sent to the same place as
stdout(1)
has been assigned to, which we already know is /dev/null
, so nothing
shows up.
Often this last statement is what I typically want, especially in a crontab instruction. Obviously another choice, and perhaps better depending on your circumstances, would be to direct output and errors to a log file and nothing to the screen, which is easily done with the command below.
kevin@localhost:~/tmp$ ls -ld /tmp /not >./logfile.txt 2>&1
If you are still confused, think about the following: 2>&1
does not create a
permanent
hard link between the stderr
and stdout
file descriptors.
2>&1
only changes stderr
to point to that which stdout
points to at that precise moment. It does not actually make stderr
point
to stdout
itself.