Skip to content

Bash Cheat Sheet

Quick-reference for everyday shell work. Works in Bash 4+ unless noted.


Basics

# run a command
ls -la

# run as superuser (may prompt for password)
sudo apt update

# chain commands
cmd1 && cmd2     # run cmd2 if cmd1 succeeds
cmd1 || cmd2     # run cmd2 if cmd1 fails
cmd1 ; cmd2      # always run cmd2
pwd                       # print working directory
ls -lah                   # list (long, all, human sizes)
cd /path/to/dir           # change directory
cd -                      # back to previous dir
mkdir -p dir/sub          # make directories (parents)
touch file                # create empty file or update mtime
cp -r src dst             # copy (recursively)
mv src dst                # move/rename
rm file                   # remove file
rm -rf dir                # remove recursively, force
ln -s target linkname     # symlink

Viewing & Editing

cat file                  # print
tac file                  # reverse
nl file                   # with line numbers
less file                 # pager (q to quit, /search)
head -n 20 file           # first 20 lines
tail -f file              # follow
wc -lwm file              # counts (lines, words, bytes)

Permissions & Ownership

chmod u+rwx,g+rx,o-r file   # change perms
chmod 644 file              # rw-r--r--
chmod 755 script.sh         # rwxr-xr-x
chown user:group path       # change owner
umask 022                   # default perms mask

Redirection & Pipes

cmd > out.txt           # stdout to file (overwrite)
cmd >> out.txt          # append
cmd 2> err.txt          # stderr to file
cmd > all.txt 2>&1      # both to file
cmd < in.txt            # stdin from file
cmd1 | cmd2             # pipe stdout of cmd1 to cmd2
tee out.txt             # split to screen + file

Process Control

&                        # run in background: cmd &
jobs                     # list background jobs
fg %1                    # bring job to foreground
bg %1                    # resume in background
Ctrl+C                   # interrupt
Ctrl+Z                   # suspend
kill -TERM <pid>         # graceful stop
kill -9 <pid>            # force kill (SIGKILL)
ps aux | grep name       # list processes
pgrep -fl pattern        # find pids
top / htop               # live view
time cmd                 # measure runtime

Finding Things

# by name (case-insensitive), from current dir
find . -iname '*.log'

# by size / mtime / type
find /var/log -type f -size +10M -mtime -7 -print

# execute on matches
find . -name '*.tmp' -print -delete

# text search
grep -Rni --include='*.py' 'pattern' .
rg 'pattern'             # ripgrep (faster, if installed)

Compression & Archives

tar -czf out.tgz dir/          # create gzipped tar
tar -xzf out.tgz               # extract
zip -r out.zip dir/            # zip
unzip out.zip                  # unzip

Variables & Environment

FOO=bar               # set (current shell)
export PATH="$HOME/bin:$PATH"   # env var for children
echo "$HOME"          # expand
printenv | sort       # list env
set -o allexport; source .env; set +o allexport  # load .env

Quoting & Expansion

echo "$var"           # expand variables, preserve spaces
echo '$var'           # literal $var
echo "sum=$((2+3))"   # arithmetic $(( ... ))

# brace expansion
echo file_{a,b,c}.txt       # file_a.txt file_b.txt file_c.txt
echo {1..5}                 # 1 2 3 4 5

Globbing (Wildcards)

*.txt                  # all .txt
file?.txt              # one character
file[0-9].txt          # a digit
shopt -s globstar
echo **/*.md           # recurse (Bash 4+)

Command Substitution

now="$(date +%F_%H-%M)"
echo "backup_$now"
files=$(ls *.log)      # capture output

Exit Codes

echo $?                # exit status of last cmd (0=success)
set -e                 # exit on error in scripts
set -u                 # error on unset variables
set -o pipefail        # fail pipeline on first error

Conditionals

# file tests: -e exists, -f file, -d dir, -s non-empty, -x executable
if [[ -f "$path" && -s "$path" ]]; then
  echo "file exists and non-empty"
elif [[ -d "$path" ]]; then
  echo "it's a directory"
else
  echo "nope"
fi

# string tests: -z empty, -n non-empty, == pattern match, =~ regex
if [[ -n "$name" && "$name" =~ ^[A-Z] ]]; then
  echo "starts with capital"
fi

# numbers: -eq -ne -lt -le -gt -ge
if (( x >= 10 )); then echo "big"; fi

Loops

# for
for f in *.txt; do
  echo "processing $f"
done

# while
while read -r line; do
  echo "$line"
done < file.txt

# C-style
for ((i=0; i<5; i++)); do echo "$i"; done

Functions

myfunc() {
  local name="${1:-world}"
  echo "hi $name"
}

result="$(myfunc Alice)"

Arrays & Assoc Arrays

# indexed
arr=(a b c)
echo "${arr[1]}"            # b
echo "${#arr[@]}"           # length
arr+=("d")
printf '%s\n' "${arr[@]}"   # iterate

# associative (Bash 4+)
declare -A m
m[color]=blue
m[shape]=circle
echo "${m[color]}"
for k in "${!m[@]}"; do echo "$k -> ${m[$k]}"; done

I/O Tricks

read -rp "Name: " name     # prompt
read -s -p "Password: " pw # silent
mapfile -t lines < file    # read lines into array
printf 'id=%04d\n' 7       # formatted output

Here-Docs & Here-Strings

cat <<'EOF' > script.sh
#!/usr/bin/env bash
echo "Hello"
EOF
chmod +x script.sh

wc -w <<< "$text"          # here-string to stdin

Useful One-Liners

# disk usage, largest first
du -sh * | sort -hr | head

# find duplicate lines
sort file | uniq -d

# random password (16 chars)
tr -dc 'A-Za-z0-9_-' </dev/urandom | head -c16; echo

Networking & Transfers

curl -I https://example.com              # headers
curl -o out.bin URL                      # download
curl -fsSL URL | bash                    # run install script (be cautious)
wget -c URL                              # continue download
ssh user@host                            # connect
scp file user@host:/path/                # copy over SSH

Git (Quick)

git status
git add -A && git commit -m "msg"
git switch -c feature-x
git pull --rebase && git push

History & Shortcuts

history | less          # show
!42                      # run command #42
!!                       # rerun last
!grep                    # rerun last starting with 'grep'
Ctrl+A / Ctrl+E          # start/end of line
Alt+B / Alt+F            # back/forward word
Ctrl+R                   # reverse search history

Scripting Template

#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'

usage() { echo "Usage: $0 [-f file] [-v]"; }

file=""
verbose=false
while getopts ":f:vh" opt; do
  case "$opt" in
    f) file="$OPTARG" ;;
    v) verbose=true ;;
    h) usage; exit 0 ;;
    \?) echo "Invalid option: -$OPTARG" >&2; exit 2 ;;
    :)  echo "Option -$OPTARG requires an argument." >&2; exit 2 ;;
  esac
done

$verbose && echo "Using file: $file"

xargs & Parallelism

printf '%s\n' *.jpg | xargs -I{} -n1 -P4 convert "{}" -resize 50% "{}"
# -n1 pass one arg per cmd, -P4 run 4 in parallel

sed & awk (Tiny Taste)

# sed: inline replace (backup .bak)
sed -i.bak 's/foo/bar/g' file

# awk: print 1st and 3rd columns if 3rd > 100
awk '$3 > 100 { print $1, $3 }' data.tsv

Trap Cleanup

tmp=$(mktemp)
cleanup() { rm -f "$tmp"; }
trap cleanup EXIT INT TERM

Debugging

bash -n script.sh        # syntax check
bash -x script.sh        # trace execution
set -x                    # start tracing in script
PS4='+ ${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: '  # prettier trace

Tips

  • Prefer [[ .. ]] over [ .. ] for safer tests.
  • Quote variable expansions: "$var" unless you explicitly want word-splitting/globbing.
  • Use shellcheck script.sh to lint scripts.
  • Keep scripts POSIX when possible (/bin/sh) for portability; use Bash when you need arrays, [[ ]], mapfile, etc.