bash error redirection

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 1
  • stderr == 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.

comments powered by Disqus