Bash Cheatsheet

I/O Redirection

Use this Bash reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Standard File Descriptors

FDNameDefault
0stdinkeyboard
1stdoutterminal
2stderrterminal

Output Redirection

cmd > file          # redirect stdout to file (overwrite)
cmd >> file         # redirect stdout to file (append)
cmd 2> file         # redirect stderr to file (overwrite)
cmd 2>> file        # redirect stderr to file (append)
cmd &> file         # redirect both stdout and stderr (Bash 4+)
cmd &>> file        # append both stdout and stderr (Bash 4+)
cmd > file 2>&1     # redirect stdout to file, then stderr to stdout (POSIX)
cmd 2>&1 > file     # WRONG ORDER: stderr goes to terminal, stdout to file
cmd > /dev/null     # discard stdout
cmd 2> /dev/null    # discard stderr
cmd &> /dev/null    # discard all output

Input Redirection

cmd < file                   # redirect file to stdin
cmd < file > outfile         # redirect both in and out
cmd <<< "string"             # here-string: redirect string to stdin
grep "foo" <<< "$variable"

# Here-document: multi-line stdin
cat <<EOF
line 1
line 2 with $var expansion
EOF

# Quoted heredoc: no expansion
cat <<'EOF'
literal $var, no $(expansion)
EOF

# Strip leading tabs (not spaces) with <<-
cat <<-EOF
	indented content
	more content
	EOF

File Descriptor Manipulation

# Duplicate FD
cmd 2>&1             # fd 2 (stderr) → same as fd 1 (stdout)
cmd 1>&2             # fd 1 (stdout) → same as fd 2 (stderr)
cmd >&2              # shorthand: stdout → stderr

# Open a file to a custom FD
exec 3> output.txt   # open output.txt on fd 3 for writing
echo "hello" >&3     # write to fd 3
exec 3>&-            # close fd 3

exec 4< input.txt    # open input.txt on fd 4 for reading
read -u 4 line       # read from fd 4
exec 4<&-            # close fd 4

# Save and restore fd 1
exec 3>&1            # save stdout to fd 3
exec 1> log.txt      # redirect stdout to log
echo "this goes to log"
exec 1>&3            # restore stdout
exec 3>&-            # close fd 3

# Read-write fd
exec 5<> /tmp/scratch   # open for both reading and writing

/dev Special Files

/dev/null             # discard reads always return EOF; writes discard
/dev/zero             # reads return infinite null bytes
/dev/random           # cryptographically secure random bytes (blocks)
/dev/urandom          # non-blocking random bytes
/dev/stdin            # fd 0
/dev/stdout           # fd 1
/dev/stderr           # fd 2
/dev/fd/N             # alias for file descriptor N
/dev/tcp/host/port    # (Bash built-in) open TCP connection
/dev/udp/host/port    # (Bash built-in) open UDP connection

# TCP via /dev/tcp
exec 3<>/dev/tcp/example.com/80
echo -e "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n" >&3
cat <&3
exec 3>&-

Pipes

cmd1 | cmd2              # pipe stdout of cmd1 to stdin of cmd2
cmd1 | cmd2 | cmd3       # pipeline chain
cmd1 |& cmd2             # pipe stdout+stderr of cmd1 (Bash 4+)
cmd1 2>&1 | cmd2         # POSIX equivalent of |&

# Pipeline exit status
echo $?                  # exit status of last command in pipeline
echo ${PIPESTATUS[@]}    # array of exit statuses of each command (Bash)
set -o pipefail          # make pipeline fail if any command fails

# Named pipe (FIFO)
mkfifo /tmp/mypipe
cmd1 > /tmp/mypipe &
cmd2 < /tmp/mypipe
rm /tmp/mypipe

Process Substitution

# Treat command output as a file
diff <(sort file1.txt) <(sort file2.txt)
comm <(sort list1) <(sort list2)
while IFS= read -r line; do
  echo "$line"
done < <(find . -name "*.sh")

# Write to a command as if it's a file
tee >(gzip > out.gz) > out.txt      # write to both gzip and a file
tee >(cmd1) >(cmd2) > /dev/null     # fan-out to multiple commands

tee — Split Output

cmd | tee file.txt             # stdout goes to both terminal AND file
cmd | tee -a file.txt          # append to file
cmd | tee file1.txt file2.txt  # write to multiple files
cmd | tee /dev/stderr | cmd2   # tee to stderr while piping to cmd2
cmd | tee >(cmd_a) >(cmd_b)    # fan-out via process substitution

Redirecting Multiple Commands

# Redirect entire block
{
  echo "first"
  echo "second"
} > output.txt

# Redirect loop output
for i in {1..5}; do
  echo "line $i"
done > output.txt

# Redirect function output
myfunc() { echo "hello"; echo "world"; }
myfunc > out.txt 2>&1

# exec to redirect all subsequent output
exec > output.log 2>&1    # all output for rest of script goes to log
exec 2> error.log         # redirect only stderr

Logging Patterns

# Log stdout and stderr separately
cmd > out.log 2> err.log

# Log stderr only, show stdout
cmd 2> err.log

# Show output AND save to log
cmd 2>&1 | tee output.log

# Timestamp each line
cmd | while IFS= read -r line; do
  echo "[$(date '+%H:%M:%S')] $line"
done | tee -a app.log

# Append with separator
echo "--- $(date) ---" >> app.log
cmd >> app.log 2>&1

Here Strings and Heredoc Advanced

# Feed heredoc to a command
mysql -u user -ppass dbname <<SQL
SELECT * FROM users WHERE active = 1;
DROP TABLE tmp_cache;
SQL

# Heredoc in a function
usage() {
  cat <<EOF
Usage: $0 [OPTIONS] <file>

Options:
  -h    show this help
  -v    verbose
EOF
}

# Variable in heredoc
name="Alice"
cat <<EOF
Dear $name,
Your account is ready.
EOF

# Literal (no expansion)
cat <<'SCRIPT'
#!/bin/bash
echo "I contain $literal variables"
SCRIPT

/proc and Special Redirects

# Read a file without spawning cat
content=$(<file.txt)          # fast: reads file content into variable

# Write without echo (slightly faster)
printf '%s\n' "content" > file.txt

# Redirect to multiple files at once (append)
{ cmd1; cmd2; } | tee -a log1.txt log2.txt > /dev/null