๐Ÿš Bash

โ† Back to Cheatsheets

A quick-reference cheatsheet for Bash scripting and the command line. Bash is both an interactive shell and a scripting language available on virtually every Unix-like system. Scripts start with #!/usr/bin/env bash and should be made executable with chmod +x script.sh.

Paradigm: Scripting ยท Command-line interface Typing: Untyped (all values are strings by default) Execution: Interpreted ยท Bash 4+ for associative arrays and case modifiers
Resources: GNU Bash manual BashGuide โ€” wooledge wiki ShellCheck โ€” linter explainshell.com devhints.io/bash โ€” cheatsheet

Variables

โ–ผ

Variables are untyped strings by default. No spaces around = when assigning. Always double-quote variable expansions ("$var") to prevent word splitting and glob expansion on values that contain spaces or special characters.

Assignment & Expansion
name="Alice"
echo "$name"          # always quote expansions
echo "${name}_suffix" # braces disambiguate the variable name
Default Values
${var:-default}  # use default if var is unset or empty
${var:=default}  # assign default if var is unset or empty
# without the colon: only triggers when var is unset, not empty
readonly & export
readonly PI=3.14
export PATH="$HOME/bin:$PATH"
export makes the variable available to child processes
Special Variables
$0        # script name
$1..$9    # positional args
$@        # all args (as array)
$#        # arg count
$?        # last exit code
$$        # current PID
Arithmetic
count=$((count + 1))
((i++))
# $((...)) is POSIX; use it instead of let or expr

Strings & Substitution

โ–ผ

Bash provides built-in string manipulation via parameter expansion โ€” no need to invoke external tools like sed or awk for simple operations. Command substitution captures the output of a command into a variable.

Length
${#var}
Substring
${var:offset:length}
# e.g. ${str:2:4} โ€” starts at index 2, takes 4 chars
Replace
${var/find/replace}   # first match
${var//find/replace}  # all matches
Strip Prefix / Suffix
${var#prefix}           # strip shortest prefix match
${var##longest_prefix}  # strip longest prefix match
${var%suffix}           # strip shortest suffix match
${var%%longest_suffix}  # strip longest suffix match
# strips shortest match; ## strips longest
Upper / Lower Case (Bash 4+)
${var^^}  # uppercase
${var,,}  # lowercase
Command Substitution
files=$(ls -1 *.txt)
today=$(date +%Y-%m-%d)
echo "Backed up on $today"
prefer $(...) over backticks โ€” nestable and easier to read

Control Flow

โ–ผ

Bash conditions test exit codes โ€” 0 means true, non-zero means false. Use [[ ... ]] (Bash built-in) instead of [ ... ] (POSIX test) โ€” it handles empty strings and spaces safely without quoting tricks, and supports &&, ||, and regex matching.

if / elif / else
if [[ "$1" == "start" ]]; then
    echo "starting"
elif [[ "$1" == "stop" ]]; then
    echo "stopping"
else
    echo "unknown command"
fi
Common Test Operators
-z "$str"      # empty string
-n "$str"      # non-empty string
-f file        # is a regular file
-d dir         # is a directory
-e path        # path exists
-r / -w / -x  # readable / writable / executable
for Loop
for file in *.txt; do
    echo "Processing $file"
done

for i in {1..5}; do echo $i; done

for ((i=0; i<10; i++)); do echo $i; done
while Loop
while IFS= read -r line; do
    echo "$line"
done < input.txt
IFS= preserves leading/trailing whitespace; -r prevents backslash interpretation
case
case "$env" in
    prod)    echo "production" ;;
    staging) echo "staging" ;;
    dev|*)   echo "development" ;;
esac
| separates multiple patterns per case; * is a catch-all

Functions

โ–ผ

Functions in Bash are just named command groups. Arguments are accessed as $1, $2, etc. โ€” there is no parameter list. Use local to scope variables to the function; without it, all variables are global by default.

Define and Call
greet() {
    local name="${1:-World}"
    echo "Hello, $name!"
}

greet "Alice"   # Hello, Alice!
greet           # Hello, World!
Return Values
is_even() {
    (( $1 % 2 == 0 ))  # exit code 0 = true
}

if is_even 4; then echo "even"; fi

# capture output instead
get_user() { echo "alice"; }
user=$(get_user)
functions return exit codes 0โ€“255, not values; capture stdout for strings

Arrays

โ–ผ

Bash supports indexed and associative arrays. Always quote array expansions with "${arr[@]}" to preserve elements that contain spaces. "${arr[*]}" joins all elements into one string.

Indexed Array
fruits=("apple" "banana" "cherry")
echo "${fruits[0]}"        # apple
echo "${fruits[@]}"        # all elements
echo "${#fruits[@]}"       # length: 3
fruits+=("date")           # append
for f in "${fruits[@]}"; do echo "$f"; done
Associative Array (Bash 4+)
declare -A colors
colors[red]="#ff0000"
colors[green]="#00ff00"
echo "${colors[red]}"
for key in "${!colors[@]}"; do
    echo "$key = ${colors[$key]}"
done

I/O & Redirection

โ–ผ

Every process has three standard file descriptors: stdin (0), stdout (1), and stderr (2). Redirection operators route these streams to files or other commands. Pipes chain commands so stdout of one becomes stdin of the next.

Redirect stdout / stderr
cmd > out.txt         # overwrite
cmd >> out.txt        # append
cmd 2> err.txt        # stderr only
cmd > out.txt 2>&1  # stdout + stderr together
Discard Output
cmd > /dev/null 2>&1
send both streams to the void
Pipe
ps aux | grep nginx | awk '{print $2}'
cat access.log | sort | uniq -c | sort -rn | head -20
Here-doc
cat <<EOF
line one
line two
EOF

# indented here-doc (Bash 4+, strips leading tabs)
cat <<-EOF
	line one
	line two
EOF
Read Input
read -rp "Enter name: " name
read -rs -p "Password: " pass   # -s = silent, no echo
-r = raw, no backslash processing; -p = inline prompt

Processes & Jobs

โ–ผ

Bash can run commands in the background, wait for them, and check their exit codes. The trap built-in lets you handle signals and run cleanup code when a script exits.

Background & Wait
long_task &
pid=$!
wait $pid   # wait for specific PID
$! holds the PID of the last background command
Exit Code
cmd && echo "ok" || echo "failed"
cmd; echo "exit: $?"
trap (Cleanup on Exit)
tmpfile=$(mktemp)
trap "rm -f $tmpfile" EXIT

# also handle Ctrl-C
trap "echo 'interrupted'; exit 1" INT TERM
EXIT fires on any exit โ€” normal or signal; safest place for cleanup

Common Patterns

โ–ผ

A few idioms that make scripts more robust and maintainable. set -euo pipefail should be at the top of almost every script โ€” it prevents silent failures that are hard to debug later.

Strict Mode
#!/usr/bin/env bash
set -euo pipefail
# -e  exit on error
# -u  error on unset variables
# -o pipefail  pipe fails if any command fails
Script Directory
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# use $DIR to reference files relative to the script
Require a Command
if ! command -v docker &> /dev/null; then
    echo "docker is required but not installed" >&2
    exit 1
fi
Check if Running as Root
if [[ "$EUID" -ne 0 ]]; then
    echo "Run as root" >&2
    exit 1
fi
Parse Flags with getopts
while getopts "vf:" opt; do
    case $opt in
        v) verbose=true ;;
        f) file="$OPTARG" ;;
        *) echo "Usage: $0 [-v] [-f file]" >&2; exit 1 ;;
    esac
done
shift $((OPTIND - 1))  # remaining positional args