Bash Cheatsheet

I/O Redirection

Use this Bash reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

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