Shell Scripting Quick Reference

Bash Scripting Cheat Sheet

Essential, battle-tested syntax reference for writing robust, fail-safe Bash and POSIX shell scripts. Includes strict mode boilerplate, parameter expansions, loops, regex conditionals, and automatic cleanup traps.

Showing 39 of 39 recipes
Bash 4+ & 5+
POSIX Compliant

Safe Bash Script Header (Unofficial Strict Mode)

strict

Enforces strict execution: -e exits immediately on error, -u treats unset variables as errors, -o pipefail preserves pipeline exit codes, and IFS prevents dangerous whitespace splitting.

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

echo "Running safe script under $(bash --version | head -n1)"
Essential best practice for every production Bash script to prevent silent failures and disastrous accidental commands (like rm -rf $UNSET_VAR/*).

Portable Shebang

strict

Uses the system's PATH environment to locate the bash binary, ensuring compatibility across macOS, Debian, Alpine, and RHEL.

Bash Syntax
#!/usr/bin/env bash
Avoid hardcoded #!/bin/bash which fails in environments where Bash is installed in /usr/local/bin or /opt/homebrew/bin.

Script Directory Resolution (CD to Script Location)

strict

Resolves the absolute path of the directory containing the currently executing script, regardless of where the user invoked it from.

Bash Syntax
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
Example:
cd "$SCRIPT_DIR"
source "$SCRIPT_DIR/config.env"

Debug Mode (Print Commands as They Execute)

strict

Prints each command with expanded variables to stderr preceded by a '+' before execution. Invaluable for step-by-step troubleshooting.

Bash Syntax
set -x  # enable trace
# commands...
set +x  # disable trace

Default Value If Unset or Empty

expansion

Returns VAR if defined and non-empty; otherwise evaluates and returns 'default_value'. Does not mutate VAR.

Bash Syntax
${VAR:-"default_value"}
Example:
PORT="${PORT:-8080}"
echo "Server listening on port $PORT"

Assign Default Value If Unset

expansion

If VAR is unset or empty, assigns 'default_value' to VAR and returns it.

Bash Syntax
${VAR:="default_value"}
Example:
echo "${APP_ENV:=production}" # Sets APP_ENV to production if blank

Mandatory Variable Assertion (Error If Unset)

expansion

If variable is unset or empty, prints the custom error message to stderr and immediately terminates the script with exit status 1.

Bash Syntax
${DATABASE_URL:?"Error: DATABASE_URL must be configured"
Example:
: "${AWS_ACCESS_KEY_ID:?Required AWS credentials missing}"

String Length

expansion

Returns the number of characters in the string contained in VAR.

Bash Syntax
${#VAR}
Example:
API_KEY="secret-123"
echo "Key length: ${#API_KEY}" # Output: 10

Remove Shortest Prefix Pattern

expansion

Strips the shortest matching pattern from the beginning of the string.

Bash Syntax
${VAR#pattern}
Example:
FILE="archive.tar.gz"
echo "${FILE#*.}" # Output: tar.gz

Remove Longest Prefix Pattern

expansion

Strips the longest matching pattern from the beginning of the string (e.g. basename extraction).

Bash Syntax
${VAR##pattern}
Example:
PATH_NAME="/usr/local/bin/node"
echo "${PATH_NAME##*/}" # Output: node

Remove Shortest Suffix Pattern

expansion

Strips the shortest matching pattern from the end of the string (e.g. extension removal).

Bash Syntax
${VAR%pattern}
Example:
FILENAME="backup.2026.sql"
echo "${FILENAME%.sql}" # Output: backup.2026

Remove Longest Suffix Pattern

expansion

Strips the longest matching pattern from the end of the string.

Bash Syntax
${VAR%%pattern}
Example:
FILE="archive.tar.gz"
echo "${FILE%%.*}" # Output: archive

Global Search and Replace

expansion

Replaces every occurrence of 'search' pattern with 'replace' string. Use single slash ${VAR/search/replace} to replace only the first occurrence.

Bash Syntax
${VAR//search/replace}
Example:
URL="https://example.com/api/v1"
echo "${URL//api/internal}" # Output: https://example.com/internal/v1

Convert to Uppercase / Lowercase

expansion

Converts all characters in VAR to uppercase (^^) or lowercase (,,) natively in Bash 4+ without external tr or awk calls.

Bash Syntax
${VAR^^}  # UPPERCASE
${VAR,,}  # lowercase
Example:
ROLE="admin"
echo "${ROLE^^}" # Output: ADMIN

Substring Extraction (Offset & Length)

expansion

Extracts a substring starting at 0-indexed offset for the specified character length.

Bash Syntax
${VAR:offset:length}
Example:
HASH="a1b2c3d4e5f6"
echo "${HASH:0:7}" # Output: a1b2c3d (git short sha)

Check If String Is Empty or Non-Empty

conditionals

-z tests for zero length (empty string or unset). -n tests for non-zero length (has content).

Bash Syntax
if [[ -z "$STR" ]]; then
  echo "String is empty"
fi

if [[ -n "$STR" ]]; then
  echo "String has content"
fi
Always quote strings inside test brackets to handle spaces safely.

Regex Pattern Matching in Bash

conditionals

The '=~' operator performs regex evaluation. Matching groups are stored in the BASH_REMATCH array.

Bash Syntax
if [[ "$INPUT" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
  echo "Valid YYYY-MM-DD date"
fi
Example:
if [[ "$EMAIL" =~ ^([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$ ]]; then
  echo "User: ${BASH_REMATCH[1]}, Domain: ${BASH_REMATCH[2]}"
fi

File & Directory Existence Tests

conditionals

Core file test operators for filesystem inspection.

Bash Syntax
[[ -f "$FILE" ]]  # Regular file exists
[[ -d "$DIR" ]]   # Directory exists
[[ -e "$PATH" ]]  # File or dir exists
[[ -s "$FILE" ]]  # File exists and is NOT empty (> 0 bytes)
[[ -x "$FILE" ]]  # File exists and is executable
[[ -L "$LINK" ]]  # Symbolic link
Combine with '!' for negation, e.g., if [[ ! -f '$CONFIG' ]]; then ... fi

Numeric Comparisons in [[ ... ]]

conditionals

Use integer test operators inside [[ ... ]], or arithmetic syntax (( A > B )).

Bash Syntax
[[ "$A" -eq "$B" ]]  # Equal
[[ "$A" -ne "$B" ]]  # Not equal
[[ "$A" -lt "$B" ]]  # Less than
[[ "$A" -le "$B" ]]  # Less than or equal
[[ "$A" -gt "$B" ]]  # Greater than
[[ "$A" -ge "$B" ]]  # Greater than or equal
Example:
COUNT=5
if (( COUNT >= 5 )); then
  echo "Threshold reached"
fi

Case Statement (Pattern Matching)

conditionals

Clean multi-branch conditional matching against strings and glob patterns.

Bash Syntax
case "$1" in
  start|run)
    echo "Starting service..."
    ;;
  stop)
    echo "Stopping service..."
    ;;
  restart)
    echo "Restarting..."
    ;;
  *)
    echo "Usage: $0 {start|stop|restart}" >&2
    exit 1
    ;;
esac

Safely Read File Line-by-Line (No Trim Traps)

loops

The safest way to process lines. 'IFS=' prevents leading/trailing whitespace trimming; '-r' disables backslash escaping; '[[ -n "$line" ]]' ensures the last line is processed even if it lacks a trailing newline.

Bash Syntax
while IFS= read -r line || [[ -n "$line" ]]; do
  echo "Processing: $line"
done < "data.txt"

For Loop Over Array Elements

loops

Iterates over array items. Always use quotes and [@]: "${array[@]}" preserves elements containing spaces.

Bash Syntax
servers=("web-1" "web-2" "api-1")

for server in "${servers[@]}"; do
  echo "Deploying to $server..."
done

Numeric Range For Loop

loops

C-style numeric loop or brace expansion for sequential ranges.

Bash Syntax
for (( i = 1; i <= 5; i++ )); do
  echo "Attempt $i of 5"
done

# Or syntax with braces:
for i in {1..5}; do
  echo "Step $i"
done

Iterate Over Files Matching Glob Safely

loops

Setting 'nullglob' prevents the loop from executing with the literal string '/var/log/*.log' when no matching files exist.

Bash Syntax
shopt -s nullglob  # avoid literal string if no files match
for file in /var/log/*.log; do
  echo "Archiving $file"
done
shopt -u nullglob

Retry Command With Exponential Backoff

loops

Repeats a command until it succeeds (exit status 0) or hits the max attempt limit, doubling the delay between tries.

Bash Syntax
max_attempts=5
attempt=1
delay=2

until curl -sSf "https://api.example.com/health" > /dev/null; do
  if (( attempt >= max_attempts )); then
    echo "Health check failed after $max_attempts attempts" >&2
    exit 1
  fi
  echo "Attempt $attempt failed. Retrying in ${delay}s..."
  sleep "$delay"
  (( attempt++ ))
  (( delay *= 2 ))
done

Indexed Array Definition & Operations

arrays

Indexed arrays in Bash. Always reference with curly braces.

Bash Syntax
arr=("alpha" "beta" "gamma")

echo "${arr[0]}"        # First item (alpha)
echo "${arr[-1]}"       # Last item (gamma)
echo "${#arr[@]}"       # Array length (3)
echo "${arr[@]}"        # All items

arr+=("delta")          # Append item
unset 'arr[1]'          # Remove element at index 1

Associative Array (Key-Value Dictionary in Bash 4+)

arrays

Hash map / key-value dictionary support via 'declare -A'. Keys can be arbitrary strings.

Bash Syntax
declare -A config

config["host"]="127.0.0.1"
config["port"]="5432"
config["db"]="production"

echo "Connecting to ${config["host"]}:${config["port"]}"

# Iterate over keys:
for key in "${!config[@]}"; do
  echo "$key = ${config[$key]}"
done

Split String into Array (Delimited by Comma)

arrays

Uses 'read -a' with a custom IFS to parse comma-separated or delimiter-separated strings directly into a Bash array.

Bash Syntax
CSV="apple,banana,orange,grape"
IFS=',' read -r -a fruits <<< "$CSV"

for fruit in "${fruits[@]}"; do
  echo "Fruit: $fruit"
done

Join Array Elements into Delimited String

arrays

Using ${arr[*]} combined with a localized IFS expands the elements joined by the first character of IFS.

Bash Syntax
arr=("one" "two" "three")
joined=$(IFS=, ; echo "${arr[*]}")
echo "$joined" # Output: one,two,three

Function Declaration with Local Variables

functions

Declare functions cleanly and always use 'local' for variables inside functions to avoid polluting global state.

Bash Syntax
log_message() {
  local level="${1:-INFO}"
  local message="${2:-}"
  local timestamp
  timestamp="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
  
  echo "[$timestamp] [$level] $message" >&2
}

log_message "ERROR" "Database connection timeout"

Special Variables Reference Table

functions

Crucial special parameter tokens in shell scripts.

Bash Syntax
$0      # Name of script
$1..$9  # Positional arguments 1 through 9
${10}  # Positional argument 10+ (requires curly braces)
$#      # Total count of positional arguments passed
$@      # All arguments as separate quoted words ("$1" "$2" ...)
$*      # All arguments joined into single string ("$1 $2 ...")
$?      # Exit code of the most recent foreground command
$$      # Process ID (PID) of the current shell script
$!      # Process ID (PID) of the last backgrounded job

Parse Command-Line Flags (getopts)

functions

Standard POSIX options parser for single-character command flags. Colon after letter (e:) indicates it expects an argument value in $OPTARG.

Bash Syntax
while getopts "e:p:vh" opt; do
  case "$opt" in
    e) ENV="$OPTARG" ;;
    p) PORT="$OPTARG" ;;
    v) VERBOSE=1 ;;
    h) echo "Usage: $0 [-e env] [-p port] [-v]"; exit 0 ;;
    *) echo "Invalid option" >&2; exit 1 ;;
  esac
done
shift $((OPTIND - 1))

Redirect Output & Errors (Cheatsheet)

redirection

Standard file descriptor numbers: 0 = stdin, 1 = stdout, 2 = stderr.

Bash Syntax
command > file         # Overwrite stdout to file
command >> file        # Append stdout to file
command 2> file        # Overwrite stderr to file
command 2>> file       # Append stderr to file
command &> file        # Redirect both stdout & stderr to file (Bash)
command > file 2>&1    # Portable POSIX redirect stdout & stderr
command > /dev/null 2>&1 # Discard all output completely

Heredoc (Multiline Text & Configuration)

redirection

Writes multiline text to a file. Quoting the delimiter 'EOF' prevents variable and command expansion inside the block.

Bash Syntax
cat << 'EOF' > /tmp/nginx.conf
server {
    listen 80;
    server_name example.com;
    location / {
        proxy_pass http://localhost:3000;
    }
}
EOF

Process Substitution (Compare Two Outputs)

redirection

Runs commands and presents their output as temporary named file descriptors (/dev/fd/N), enabling commands expecting files to read command output directly.

Bash Syntax
diff -u <(curl -s "https://api.v1.com/data") <(curl -s "https://api.v2.com/data")

Tee (Log to File While Still Displaying in Terminal)

redirection

Duplicates stdout stream so you can watch live terminal output while simultaneously saving a full timestamped log.

Bash Syntax
make build 2>&1 | tee build.log

Trap on Script Exit (Automatic Temp Directory Cleanup)

traps

Guarantees cleanup runs whenever the script exits, even if interrupted by error or Ctrl+C. The most robust pattern for temporary resources.

Bash Syntax
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT

# Script operations...
echo "Working in $tmpdir"
# Cleanup runs automatically whether script succeeds or crashes!

Standard Linux Exit Codes Meaning

traps

Linux standard exit status codes. Scripts should always return 0 on success and >0 on failure.

Bash Syntax
0    # Success (no error)
1    # Catchall for general errors
2    # Misuse of shell built-ins (syntax errors)
126  # Command invoked cannot execute (permissions problem)
127  # Command not found (PATH issue or typo)
128  # Invalid argument to exit
130  # Script terminated by Ctrl+C (SIGINT = 128 + 2)
137  # Script terminated by SIGKILL (128 + 9, e.g. Linux OOM killer)
143  # Script terminated by SIGTERM (128 + 15)

Prompt User for Confirmation [y/N]

traps

Interactive confirmation guard that defaults safely to No if the user presses Enter without typing 'y'.

Bash Syntax
read -r -p "Are you sure you want to deploy to production? [y/N] " response
case "$response" in
  [yY][eE][sS]|[yY])
    echo "Proceeding..."
    ;;
  *)
    echo "Aborted by user."
    exit 1
    ;;
esac

Related Developer & DevOps Tools

Supercharge your terminal workflow with complementary client-side utilities from the DevToolkit Hub catalog:

Frequently Asked Questions About Bash Scripting

Why should every Bash script start with 'set -euo pipefail'?

'set -euo pipefail' is commonly known as unofficial Bash Strict Mode. '-e' causes the script to abort immediately if any command exits with a non-zero status. '-u' throws an error and halts execution if an unset variable is referenced, preventing catastrophic bugs like 'rm -rf $DIR/*' when $DIR is typoed. '-o pipefail' ensures that pipelines return the exit code of the last failing command in the pipe rather than masking errors when the final command succeeds. Finally, setting 'IFS=$'\n\t'' prevents unintended word splitting on spaces.

What is the difference between single brackets [ ... ] and double brackets [[ ... ]] in Bash?

Single brackets '[ ... ]' invoke the POSIX standard test utility, which is subject to pathname expansion and word splitting, requiring extensive quoting. Double brackets '[[ ... ]]' are a native Bash keyword enhancement that prevents word splitting, supports regular expression matching via the '=~' operator, and allows logical '&&' and '||' operators directly inside the conditional.

How do I safely read a text file line-by-line without trimming whitespace?

Use 'while IFS= read -r line || [[ -n "$line" ]]; do ... done < file.txt'. Clearing IFS ('IFS=') stops leading and trailing whitespace from being stripped. The '-r' flag disables backslash interpretation. Checking '[[ -n "$line" ]]' ensures the final line of the file is processed even if it does not end with a trailing newline.

What does ${VAR:-default} mean compared to ${VAR:=default}?

'${VAR:-default}' evaluates and returns 'default' if VAR is empty or unset, leaving VAR unmodified. In contrast, '${VAR:=default}' evaluates 'default' and simultaneously assigns it into VAR, persisting the value for subsequent commands in the script.

How do I clean up temporary files reliably when a Bash script finishes or crashes?

Use the 'trap' built-in targeting the EXIT pseudo-signal. For example: 'tmpdir=$(mktemp -d); trap 'rm -rf "$tmpdir"' EXIT'. This guarantees that the removal command is triggered automatically whenever the script exits, whether it terminates cleanly, errors out under 'set -e', or receives an interruption signal.