Shell Scripting Cheat Sheet
Script Setup
Section titled “Script Setup”#!/usr/bin/env bashset -euo pipefail # Exit on error, undefined var, pipe failure| Flag | Effect |
|---|---|
-e | Exit immediately on command failure |
-u | Error on undefined variables |
-o pipefail | Pipe fails if any command fails |
-x | Print commands as executed (debug) |
Variables
Section titled “Variables”# Assignment (no spaces around =)name="alice"count=42
# Usageecho "Hello, $name"echo "Count: ${count}"
# Default values${var:-default} # Use default if unset/empty${var:=default} # Set and use default if unset/empty${var:+alt} # Use alt if var IS set${var:?error msg} # Exit with error if unset/emptyCommand Substitution
Section titled “Command Substitution”$() runs a command and substitutes its output. Use it to capture results into
variables or embed output in strings.
# Assign output to a variablecurrent_branch=$(git branch --show-current)
# Embed in a stringtar czf "backup-$(date +%Y%m%d).tar.gz" ./src
# Capture output and check statusif output=$(git pull 2>&1); then echo "Success: $output"else echo "Failed: $output"fi
# Capture exit codesome_commandstatus=$?See CLI Pipelines for
$()used in pipeline composition — inline arguments, nesting, and fzf selection.
String Operations
Section titled “String Operations”str="hello world"
${#str} # Length: 11${str:0:5} # Substring: "hello"${str:6} # From position: "world"${str/world/there} # Replace first: "hello there"${str//o/O} # Replace all: "hellO wOrld"${str#hello } # Remove prefix: "world"${str%world} # Remove suffix: "hello "${str^^} # Uppercase: "HELLO WORLD"${str,,} # Lowercase: "hello world"Arrays
Section titled “Arrays”# Declarefruits=("apple" "banana" "cherry")
# Accessecho "${fruits[0]}" # First elementecho "${fruits[@]}" # All elementsecho "${#fruits[@]}" # Lengthecho "${!fruits[@]}" # All indices
# Modifyfruits+=("date") # Appendfruits[1]="blueberry" # Replaceunset fruits[2] # Remove
# Iteratefor fruit in "${fruits[@]}"; do echo "$fruit"doneConditionals
Section titled “Conditionals”If Statements
Section titled “If Statements”if [[ condition ]]; then # commandselif [[ condition ]]; then # commandselse # commandsfiTest Operators
Section titled “Test Operators”Strings:
| Operator | Meaning |
|---|---|
-z "$s" | String is empty |
-n "$s" | String not empty |
"$a" = "$b" | Strings equal |
"$a" != "$b" | Strings differ |
"$a" < "$b" | Alphabetically less |
Numbers:
| Operator | Meaning |
|---|---|
$a -eq $b | Equal |
$a -ne $b | Not equal |
$a -lt $b | Less than |
$a -le $b | Less or equal |
$a -gt $b | Greater than |
$a -ge $b | Greater or equal |
Files:
| Operator | Meaning |
|---|---|
-e file | Exists |
-f file | Regular file |
-d file | Directory |
-r file | Readable |
-w file | Writable |
-x file | Executable |
-s file | Size > 0 |
f1 -nt f2 | f1 newer than f2 |
Logic:
[[ cond1 && cond2 ]] # AND[[ cond1 || cond2 ]] # OR[[ ! condition ]] # NOTCase Statements
Section titled “Case Statements”case "$input" in start|begin) echo "Starting..." ;; stop|end) echo "Stopping..." ;; *) echo "Unknown command" ;;esacFor Loop
Section titled “For Loop”# Over listfor item in apple banana cherry; do echo "$item"done
# Over arrayfor item in "${array[@]}"; do echo "$item"done
# C-stylefor ((i=0; i<10; i++)); do echo "$i"done
# Over filesfor file in *.txt; do echo "$file"done
# Over command outputfor user in $(cut -d: -f1 /etc/passwd); do echo "$user"doneWhile Loop
Section titled “While Loop”# Countercount=0while [[ $count -lt 5 ]]; do echo "$count" ((count++))done
# Read lines from filewhile IFS= read -r line; do echo "$line"done < input.txt
# Read lines from command (see CLI Pipelines for process substitution)while IFS= read -r line; do echo "$line"done < <(some_command)Loop Control
Section titled “Loop Control”break # Exit loopcontinue # Skip to next iterationFunctions
Section titled “Functions”# Definitiongreet() { local name="$1" # Local variable local greeting="${2:-Hello}" # With default echo "$greeting, $name!" return 0 # Exit status}
# Usagegreet "Alice" # "Hello, Alice!"greet "Bob" "Hi" # "Hi, Bob!"result=$(greet "Carol") # Capture outputSpecial Variables in Functions
Section titled “Special Variables in Functions”| Variable | Meaning |
|---|---|
$0 | Script name |
$1-$9 | Positional arguments |
$@ | All arguments (as array) |
$* | All arguments (as string) |
$# | Number of arguments |
$? | Last command exit status |
$$ | Current script PID |
Input/Output
Section titled “Input/Output”Read Input
Section titled “Read Input”# Basicread -r nameecho "Hello, $name"
# With promptread -rp "Enter name: " name
# Silent (passwords)read -rsp "Password: " pass
# With timeoutread -rt 5 -p "Quick! " answerHere Documents and Here Strings
Section titled “Here Documents and Here Strings”# Here document — multi-line input with variable expansioncat <<EOFLine 1Line 2 with $variable expansionEOF
# Quoted delimiter — suppress expansioncat <<'EOF'Line with $literal dollar signsEOF
# Indented (<<- strips leading tabs)if true; then cat <<-EOF indented body EOFfi
# Here string — single-line inputgrep "error" <<< "$log_output"bc <<< "2 ^ 10" # 1024Redirection
Section titled “Redirection”cmd > file # Stdout to file (overwrite)cmd >> file # Stdout to file (append)cmd 2> file # Stderr to filecmd &> file # Both stdout and stderrcmd 2>&1 # Stderr to stdoutcmd < file # File to stdincmd <<< "string" # String to stdinError Handling
Section titled “Error Handling”# Check exit statusif ! command; then echo "Command failed" exit 1fi
# Trap errorstrap 'echo "Error on line $LINENO"; exit 1' ERR
# Cleanup on exitcleanup() { rm -f "$tmpfile"}trap cleanup EXIT
# Custom error functiondie() { echo "Error: $*" >&2 exit 1}
[[ -f "$file" ]] || die "File not found: $file"Common Patterns
Section titled “Common Patterns”Argument Parsing
Section titled “Argument Parsing”while [[ $# -gt 0 ]]; do case "$1" in -v|--verbose) verbose=true shift ;; -f|--file) file="$2" shift 2 ;; --) shift break ;; -*) die "Unknown option: $1" ;; *) args+=("$1") shift ;; esacdoneCheck Dependencies
Section titled “Check Dependencies”require() { command -v "$1" >/dev/null 2>&1 || die "$1 required but not found"}
require curlrequire jqTemporary Files
Section titled “Temporary Files”tmpfile=$(mktemp)trap 'rm -f "$tmpfile"' EXIT
echo "data" > "$tmpfile"Progress Indicator
Section titled “Progress Indicator”spinner() { local pid=$1 local chars="/-\|" while kill -0 "$pid" 2>/dev/null; do for ((i=0; i<${#chars}; i++)); do printf "%s" "${chars:$i:1}" sleep 0.1 done done printf ""}
long_command &spinner $!See Also
Section titled “See Also”- CLI Pipelines — Pipes, xargs, fzf, process substitution, and composing commands into data streams
- Cryptography — openssl commands for scripting
- Regex — Pattern matching in scripts
- Unix — Individual commands used in scripts