Shell Mastery — File Descriptors

The Three Default File Descriptors

Every process gets three at birth:

0 = stdin   (keyboard input)
1 = stdout  (normal output)
2 = stderr  (error output)
# See them live
ls -la /proc/$$/fd
# 0 -> /dev/pts/0  (your terminal)
# 1 -> /dev/pts/0
# 2 -> /dev/pts/0

Redirection

Basics
cmd > file          # stdout to file (same as 1>file)
cmd 2> file         # stderr to file
cmd &> file         # both stdout + stderr to file
cmd > file 2>&1     # same thing — stdout to file, stderr follows stdout
cmd >> file         # append stdout
cmd 2>> file        # append stderr
Discard output
cmd > /dev/null           # discard stdout
cmd 2> /dev/null          # discard stderr
cmd &> /dev/null          # discard everything
cmd > /dev/null 2>&1      # same — portable POSIX form
Separate stdout and stderr into different files
cmd > stdout.log 2> stderr.log
Swap stdout and stderr
cmd 3>&1 1>&2 2>&3 3>&-
# 3=copy of stdout, 1→stderr, 2→old stdout, close 3

Custom File Descriptors (3-9)

Open fd 3 for writing
exec 3> /tmp/custom.log
echo "line 1" >&3
echo "line 2" >&3
exec 3>&-                  # close fd 3
cat /tmp/custom.log
Open fd 4 for reading
exec 4< /etc/passwd
while IFS= read -r line <&4; do
    echo "$line"
done
exec 4<&-                  # close fd 4
Read and write on same fd
exec 5<> /tmp/data.txt     # open for read+write
echo "hello" >&5           # write
cat <&5                    # read
exec 5>&-                  # close
Log to file AND terminal simultaneously
exec 3>&1                  # save stdout to fd 3
exec 1> >(tee /tmp/script.log) 2>&1   # tee stdout+stderr
echo "This goes to terminal AND log"
exec 1>&3 3>&-             # restore stdout

Inspect File Descriptors

See what fds a process has open
ls -la /proc/$$/fd         # current shell
ls -la /proc/self/fd       # same thing
ls -la /proc/<PID>/fd      # any process
Count open fds
ls /proc/$$/fd | wc -l
fd limits
ulimit -n                  # soft limit (per process)
ulimit -Hn                 # hard limit
cat /proc/sys/fs/file-max  # system-wide max
cat /proc/sys/fs/file-nr   # allocated  free  max
Detailed info about a specific fd
cat /proc/$$/fdinfo/0      # flags, position for fd 0 (stdin)

Competition Patterns

Capture exit code while piping stderr
{ cmd 2>&1; echo $? > /tmp/exit_code; } | tee output.log
rc=$(cat /tmp/exit_code)
Process substitution uses fds under the hood
diff <(cmd1) <(cmd2)
# <() creates /dev/fd/N pipes — check:
ls -la /dev/fd/
Named pipe (fifo) — manual fd control
mkfifo /tmp/mypipe
cmd1 > /tmp/mypipe &       # writer runs in background
cmd2 < /tmp/mypipe         # reader blocks until data arrives
rm /tmp/mypipe
Here string uses fd 0 (stdin)
read -r var <<< "hello world"
echo "$var"    # hello world
Redirect inside a loop (common gotcha)
# WRONG — pipe creates subshell, variable lost
cat file.txt | while read line; do count=$((count+1)); done
echo $count    # empty!

# RIGHT — redirect, stays in current shell
while read line; do count=$((count+1)); done < file.txt
echo $count    # correct
Read from multiple files simultaneously
exec 3< file1.txt
exec 4< file2.txt
while IFS= read -r line1 <&3 && IFS= read -r line2 <&4; do
    printf "%-30s %s\n" "$line1" "$line2"
done
exec 3<&- 4<&-

Where to Find This in System Docs

man pages — the authoritative reference
man bash              # search: /REDIRECTION  (section 3.6)
                      # search: /DUPLICATING  (fd duplication)
man zshmisc           # search: /REDIRECTION
man zshall            # everything zsh in one page
Kernel-level understanding (how it actually works)
man 2 open            # open() syscall — creates a new fd
man 2 close           # close() syscall — releases a fd
man 2 dup2            # dup2() — how 2>&1 works internally
man 2 pipe            # pipe() — how | creates a pair of fds
man 2 read            # read() — how data flows through a fd
man 2 write           # write() — how data is sent to a fd
man 7 pipe            # pipe semantics, buffer sizes (64K default)
bash builtin help
help exec             # exec builtin — fd manipulation
help read             # read from specific fd
help mapfile          # read fd into array
System files
ls /proc/$$/fd                    # live view of your shell's fds
ls -la /dev/fd/                   # symlinks to /proc/self/fd/
cat /proc/$$/fdinfo/0             # detailed info on fd 0
ls /usr/share/doc/bash*/          # bash documentation on disk
The key man page search patterns
# In man bash, search for these sections:
/REDIRECTION          # all redirection syntax
/Here Documents       # heredocs
/Here Strings         # <<< syntax
/Duplicating          # 2>&1 syntax
/Moving               # fd moving with -
/Opening              # exec N<>file