Default file descriptors

0

standard input

1

standard output

2

standard error

File descriptors 3 through 9 are empty by default, but 5 can cause some trouble. These empty file descriptors may be used to temporarily copy one of the default file descriptors. One use of a copy is to redirect standard output to a file temporarily and then back to the terminal (example).

Example: duplicate standard output into file descriptor 2, so that errors are written to the standard output.

Inline or exec redirects

dash and bash both provide at least two syntaxes for manipulating file descriptors.

Example: echo one, two, three into the file numbers.txt

echo "one, two, three" 1> numbers.txt

In this inline form of redirection, standard output for the echo command is redirected into numbers.txt.

exec 1> numbers.txt
echo "one, two, three"

This exec redirection diverts standard output to numbers.txt for any command that follows it.

Child processes inherit open file descriptors

The exec example hints at the fact that Child processes inherit open file descriptors (TLDP). To further explore this concept let's try the redirection again, but accidentally leave out exec:

1> numbers.txt
echo "one, two, three"

This just creates an empty numbers.txt file and then echoes one, two, three to the terminal. The problem is that the shell spawns one child process to run the redirection, lets that process end, and then creates a new sibling child process for the echo. To run multiple commands that all inherit the redirection, we need to put the exec back where it was.

exec 1> numbers.txt
echo "one, two, three"

With the exec back in place, the shell still spawns a new process and redirects stdout to numbers.txt, but now the exec causes the new process to become the parent process for any subsequent commands. Therefore echo runs as a child process of the redirection, inherits the open file descriptors, and sends one, two, three to numbers.txt rather than the terminal.

Temporary redirections

Redirecting output first to a file and then back to the terminal can be done using either inline or exec redirects:

echo "one, two, three" 1> numbers.txt
echo "Numbers written."

In the first echo process, stdout is redirected to numbers.txt, but then that process ends. The process for the second echo has no redirections, and therefore outputs to the terminal.

Extra care must be taken when redirecting with exec due to exec's reparenting effects. One way to restore stdout to the terminal is to save the pointer to the terminal in an unused file descriptor (fd 9 in this case) before redirecting stdout. This saved copy is carried through the process (and child processes) until it is copied back to stdout (fd 1).

This example also demonstrates that multiple redirects can be made using a single exec.

Another way to re-redirect stdout to the terminal is to use the filename that represents the terminal (via the tty command in this case).

Shorthand for stdin and stdout

All of the examples on this page up to this point have explicitly provided both sides of every redirection. This might lead to some confusion because the shorthand forms of redirection are more common. Consider again the first example:

echo "one, two, three" 1> numbers.txt

A more common way to write this would be…

echo "one, two, three" > numbers.txt

The 1 may be omitted because > redirects fd 1 by default.

Similarly, the contents of a file could be read as stdin by writing…

cat 0< numbers.txt

…but the more common form would be…

cat < numbers.txt

…where file descriptor 0 is implicit.

Links

One way to get familiar with sh redirection is to implement it again using C library calls. Maybe do something like: open a pipe, fork a child process, duplicate (dup2) file descriptors as needed and close unused file descriptors, execute (execv) a program in the child process, write bytes through the pipe to the child process from the parent process, and close any remaining unneeded file descriptors.