File descriptors are very essential in programming. When you open a file, you get a token, which identifies the file instance. With this token you refer to the file, write, read or close it. You have about the same on the shell.
There are three default file descriptors:
- Standard input (STDIN)
- Standard output (STDOUT)
- Standard error (STDERR)
They are numbered from 0 to 2 and you can handle them with the input and output redirection operators
# Redirect STDIN cmd <file # Redirect stdout cmd > file
Without any number,
< uses STDIN and
> STDOUT, but you can also change this behavior:
cmd 2> file
That would redirect STDERR to the file
file. You can also append STDERR to STDOUT and redirect both at the same time:
cmd > file 3>&1
That's very basic and you should already know that. But there's more than just this. Besides 1, 2 and 3, there are also file descriptors 3 to 1023, which are free to use. For each one a symlink in
/dev/fd/ is created as soon as they are initialized.
To use these, you have to know the
exec command. This command can create, move and close file descriptors of the current process as well as replacing the shell. So to replace the currently running bash shell with a zsh, run
You can also specify any other command, but that would normally log you out of your shell.
But more important is the handling of custom file descriptors. To create a read/write file descriptor for a file run:
exec 3<> file
That creates a file descriptor with the number 3. You could also create a read-only or write-only file descriptor by just specifying
>, respectively, but note that the file must already exist for read-only descriptors. Now write to this file:
# Open file and assign descriptor exec 3<> file # Write to file echo 'foobar' >&3 # Reopen file to reset pointer exec 3<> file # Read from file <&3 cat
This would first write
foobar to the file and then read the written string. Note that reopening the file is necessary here because the write process has set the file pointer past the last line, so the read command would read an empty string.
You can close a file descriptor with
n<&- for input file descriptors and
n>&- for output file descriptors. For instance
to close the file descriptor 3.
It is also possible to copy file descriptors. This is important if you want to reassign STDOUT or STDIN. If you don't clone the file descriptor first, you wouldn't be able to restore it afterwards.
# Backup STDOUT to fd 3 exec 3>&1 # Redirect all STDOUT to file exec >file # Should now go to file echo 'test' # Restore STDOUT and close fd 3 exec >&3 3>&-
You can do the same with STDIN. Of course, you can also close STDIN and STDOUT. If you do that, you'll get an error message as soon as you try to access one of these channels.
Read more about file descriptors and
- phpman.info: exec man page
- Advanced Bash-Scripting Guide – Using exec
- GNU Bash Reference Manual – Redirections