When a shell is created it opens three files, each having an integer file descriptor.
0: Standard Input (stdin) – this is normally a keyboard
1: Standard Output (stdout) – this is typically your terminal emulator
2: Standard Error (stderr) – this is normally the same as Standard Output, but it is possible to redirect errors to a different place. This can be useful for hiding unnecessary error messages, or for saving a log of errors, and many other reasons.
The short script below (called "testme
") writes a message to standard output, then another (slightly different) message to standard error. This script uses three shell idioms:
1) The $@
variable is special, in that it contains all of the arguments passed to the script. We will use this later to show how the shell has parsed the command-line.
2) Because the entire message is in double quotes, to get the output to display some double quotes around the list of parameters, we need to use \"
to mark the "
symbol as being a character to display, rather than treating it as the end of the quoted message.
3) The final line sends its output to stderr (rather than stdout) by redirecting to file descriptor 2, which as we saw above, is stderr. It does this with the ">&2
" syntax.
The script is as follows. This is enough to be able to accurately test what happens in different situations: How the Standard and Error streams are redirected, and how the parameters to the script are parsed.
$ cat testme #!/bin/bash echo "This is Standard Output (stdout) and my parameters are \"$@\"" echo "This is Standard Error (stderr) and my parameters are \"$@\"" >&2
When we run this script, it writes output and errors to the same place. It also tells us what parameters it was called with:
$ ./testme hello world This is Standard Output (stdout) and my parameters are "hello world" This is Standard Error (stderr) and my parameters are "hello world" $
We can hide the regular (stdout) output, but still show errors, by redirecting stdout (the default stream) to the special "/dev/null
" device. "/dev/null
" simply reads anything passed to it, and discards them.
$ ./testme hello world > /dev/null This is Standard Error (stderr) and my parameters are "hello world" $
We can also choose just to hide the errors, but keep the standard output. Because stderr is file descriptor 2, we say "2> /dev/null
":
$ ./testme hello world 2> /dev/null This is Standard Output (stdout) and my parameters are "hello world" $
Be careful here, because whitespace matters. If we put "2 > /dev/null
" instead of "2> /dev/null
", then the shell interprets your command as being "testme hello world 2
" and that you want the stdout (not stderr) to be sent to /dev/null
. If you think about it, how else would you be able to pass "hello world 2
" and tell the shell to redirect stdout?
$ ./testme hello world 2 > /dev/null This is Standard Error (stderr) and my parameters are "hello world 2" $
We can also redirect both stdout and stderr to /dev/null
. Now nothing is displayed at all:
$ ./testme hello world > /dev/null 2>/dev/null $
There is also another way to say "and send stderr to wherever stdout is going" by referring to stdout as "&1
"; the shell will substitute this with /dev/null
, since that is where stdout is going:
$ ./testme hello world > /dev/null 2>&1 $
Again, spaces matter. The entire "2>&1
" must have no whitespace at all, otherwise strange things happen:
$ ./testme hello world > /dev/null 2 > &1 bash: syntax error near unexpected token `&' $
And again, because the "> /dev/null
" syntax is interpreted by the shell, if you apply the spacing like this, your stdout will be redirected (twice, as it happens) to /dev/null
, the "2
" is treated as a parameter to the script itself, and stderr is not redirected at all:
$ ./testme hello world > /dev/null 2 > /dev/null This is Standard Error (stderr) and my parameters are "hello world 2" $
Of course, you don't have to send everything to /dev/null
. You can redirect to a file, like this example, which sends stdout
to /tmp/hello.txt
. Note that
stderr
is still displayed to your terminal.
$ ./testme hello world > /tmp/hello.txt This is Standard Error (stderr) and my parameters are "hello world" $ cat /tmp/hello.txt This is Standard Output (stdout) and my parameters are "hello world" $
Similarly, you can send stdout
to /tmp/hello.txt
, and any errors to /tmp/error.log
for reference:
$ ./testme hello world > /tmp/hello.txt 2> /tmp/error.log $ cat /tmp/hello.txt This is Standard Output (stdout) and my parameters are "hello world" $ cat /tmp/error.log This is Standard Error (stderr) and my parameters are "hello world" $
You can redirect to any given file descriptor, by its integer representation, via the ">&2
" syntax, as shown in the "testme
" script itself. You can then redirect those
streams to wherever you like via the "> /dev/null
" or "2> /dev/null
" syntax, according to the output you want to redirect. The default is file descriptor 1 (stdout
).
My Shell Scripting books, available in Paperback and eBook formats. This tutorial is more of a general introduction to Shell Scripting, the longer Shell Scripting: Expert Recipes for Linux, Bash and more book covers every aspect of Bash in detail.
![]() Shell Scripting Tutorial is this tutorial, in 88-page Paperback and eBook formats. Convenient to read on the go, and in paperback format good to keep by your desk as an ever-present companion. Also available in PDF form from Gumroad:Get this tutorial as a PDF | ![]() Shell Scripting: Expert Recipes for Linux, Bash and more is my 564-page book on Shell Scripting. The first half covers all of the features of the shell in every detail; the second half has real-world shell scripts, organised by topic, along with detailed discussion of each script. |