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
Navigation & Files
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
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
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)
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
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.