When bash initialises it opens three file descriptors for STDIN, STDOUT and STDERR:

0 = STDIN --> /dev/tty0  
1 = STDOUT --> /dev/tty0  
2 = STDERR --> /dev/tty0

File descriptors always point to a file and the case of the three above, that is your terminal, typically /dev/tty0. It’s worth noting that you can create (or open) new file descriptors and you can redirect existing ones.

Line by Line Redirection

We can capture the output of some_command by using either:

$ some_command >file_name

or more explicitly by using the STDOUT file descriptor:

$ some_command 1>file_name

This has the affect of changing the file that the descriptor points to:

0 = STDIN --> /dev/tty0  
1 = STDOUT --> file_name  
2 = STDERR --> /dev/tty0

You can redirect STDERR using:

$ some_command 2>file_name

In fact you can redirect any file descriptor (n) to file_name using:

$ some_command n>file_name

Redirect STDOUT and STDERR to a file

The shortcut way to achieve this is:

$ some_command &>file_name

but using file descriptors explicitly we would achieve this by redirecting both streams to the same destination:

$ some_command >file_name 2>&1

This works by first redirecting STDOUT to the new file called file_name, then STDERR is duplicated to be the same as STDOUT. So the file descriptors now look like:

0 = STDIN --> /dev/tty0  
1 = STDOUT --> file_name  
2 = STDERR --> file_name

The order is super important. Using $ some_command 2>&1 >file_name will not have the desired effect. To understand why, check out Bash One Liners Explained.

Redirect Multiple Script Lines to a Log File

This is particulalrly useful when you want to capture the output (or errors) from running multiple commands within a script. No one has time to redirect line by line….

To capture all errors only:

$ exec 2>file_name

bad_command # Error message sent to file_name
echo "Howdy" # Howdy sent to STDOUT
bad_command # Error message appended to file_name

To capture all errors and standard output:

$ exec >file_name 2>&1 # or exec &>file_name

bad_command # Error message sent to file_name
echo "Howdy" # Howdy appended to file_name
bad_command # Error message appended to file_name

Discard STDOUT

If /dev/tty0 is the terminal /dev/null ia some other special file that just gets discarded. If for some reason you don’t want to be bothered by terminals verbosity you can just choose to ignore it with:

$ some_command > /dev/null

If you want to feel super powerful try:

$ some_command > /dev/null 2>&1 or $ some_command &> /dev/null which will ignore standard output and error messages.