Bash Custom File Descriptors
A custom file descriptor is an extra numbered channel a script opens beyond standard input, output, and error. Extra channels let one script carry separate streams, such as data and a log, without mixing them.
A log stream on descriptor 3
Two log lines go to descriptor 3, which points at a file. Nothing reaches the screen until the file is read back.
exec 3>debug.log
echo "debug: step one" >&3
echo "debug: step two" >&3
exec 3>&-
cat debug.logReading through descriptor 3
Descriptor 3 opens a file for reading, and read -u 3 pulls one line at a time from it. Standard input stays untouched.
printf 'alpha\nbeta\n' > names.txt
exec 3<names.txt
read -r -u 3 first
read -r -u 3 second
exec 3<&-
echo "first: $first"
echo "second: $second"Two streams at once
Descriptors 3 and 4 point at different files, so audit lines and result lines never mix. Each file is read back under its own label.
exec 3>audit.log
exec 4>results.txt
echo "audit: job started" >&3
echo "total: 42" >&4
exec 3>&-
exec 4>&-
echo "audit:"
cat audit.log
echo "results:"
cat results.txtExample
exec 3>trace.log
exec 4>report.txt
echo "trace: reading input" >&3
printf 'item one\nitem two\n' > items.txt
exec 5<items.txt
while read -r -u 5 item; do
echo "trace: saw $item" >&3
echo "$item" >&4
done
exec 3>&-
exec 4>&-
exec 5<&-
echo "trace:"
cat trace.log
echo "report:"
cat report.txtKey points
- Number range: descriptors 3 through 9 are free for scripts to use.
- Open for writing: exec 3>file opens descriptor 3 for writing to that file.
- Open for reading: exec 3<file opens descriptor 3 for reading from that file.
- Close when done: exec 3>&- closes descriptor 3, so later code cannot write to it by mistake.
- Separate streams: each descriptor has its own file, so log lines never pollute result data.
- Read by number: read -u 3 pulls one line from descriptor 3, not from standard input.
- Honest limit: a descriptor opened in a script is gone when the script ends.
In this example, the script opens descriptor 3 for a trace log and descriptor 4 for a report. It writes one trace line, then opens the items file on descriptor 5. A loop reads each item through descriptor 5, writes a trace line to descriptor 3, and writes the item itself to descriptor 4. All three descriptors close before the two files are read back.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Why open descriptor 3 for a log instead of printing log lines to standard output?