Open and Close Files in Bash

In a shell script, file descriptor 0 stands for stdin, file descriptor 1 stands for stdout, and file descriptor 2 stands for stderr. In addition, programmers can open, close, or duplicate file descriptors with the exec built-in command and the I/O redirection operator:

Syntax Description
exec $fd< "${filepath}" Open an input file at the given file descriptor $fd
exec $fd<&- Close the input file descriptor $fd
exec $fd> "${filepath}" Open an output file at the given file descriptor $fd
exec $fd>&- Close the output file descriptor $fd
exec $dst>&src Duplicate the file descriptor from $src to $dst. Both $dst and $src will refer to the file which was referred by $src.

Example 1: Write some message to another output file

First, open output_file.txt at file descriptor 3:

exec 3> output_file.txt

Second, redirect the stdout of the command with 1>&3 to print some messages:

echo 1>&3 "some messages"

Third, close file descriptor 3 with:

exec 3>&-

Example 2: Redirect stdout/stderr temporarily

Under some circumstances, one would like to redirect the stdout or stderr of the subsequent commands. The file descriptor duplication command will be handy in this case.

First, backup stdout and stderr with file descriptor duplication command.

exec 3>&1  # Duplicate stdout to file descriptor 3
exec 4>&2  # Duplicate stderr to file descriptor 4

Second, open files for redirection:

exec 1> stdout.log
exec 2> stderr.log

Third, run the commands as usual:

echo "some output to stdout"
echo 1>&2 "some output to stderr"

Fourth, restore the file descriptors if the shell script still needs them:

exec 1>&3  # Duplicate file descriptor 3 to stdout
exec 2>&4  # Duplicate file descriptor 4 to stderr
exec 3>&-  # Close file descriptor 3 (free the resources)
exec 4>&-  # Close file descriptor 4 (free the resources)

In this step, first two lines copy the stashed file descriptors back to stdout and stderr. Last two lines close the file descriptors.

Note: Closing a file descriptor does not necessary close a file. If two file descriptors refer to the same file, then the file will not be closed until both file descriptors are closed. In the fourth step, it is important to close file descriptor 3 and 4 to avoid resource leaks.

Example 3: Read lines from a file

First, open an input file with exec:

exec 3< input_file.txt

Second, read the file with I/O redirection 0<&3:

while read 0<&3 line; do
  echo "GOT: ${line}"
done

Third, close the file with:

exec 3<&-

Reference