Advanced โฑ 20 min Lesson 11 of 13

๐Ÿ› Error Handling & Debugging

Exit codes, strict mode, trap, and debugging techniques.

Robust Scripts with Error Handling

In real-world scripting, errors will happen. Files won't exist, commands will fail, networks will be unreachable. A good script handles these situations gracefully instead of silently producing wrong results.

Exit Codes

Every command in Bash returns an exit code (0โ€“255). By convention: 0 means success, and anything else means failure.

bash
# Check the exit code of the last command
ls /tmp
echo $?    # 0 (success)

ls /nonexistent
echo $?    # 2 (failure โ€” no such directory)

# Use exit codes in conditionals
if grep -q "pattern" file.txt; then
    echo "Pattern found"
else
    echo "Pattern not found"
fi

# Set your own exit code
exit 0     # script succeeded
exit 1     # script failed

Strict Mode: set -euo pipefail

The "unofficial Bash strict mode" is a set of options that make your scripts fail early instead of silently continuing after errors.

bash
#!/bin/bash
set -euo pipefail

# set -e  โ†’ Exit immediately if any command fails
# set -u  โ†’ Treat undefined variables as errors
# set -o pipefail โ†’ A pipeline fails if ANY command in it fails

# Without set -e:
false          # exit code 1, but script continues
echo "This still runs"

# With set -e:
false          # script exits HERE
echo "This never runs"
๐Ÿ’ก Tip: Start every production script with set -euo pipefail. It catches a huge class of bugs automatically. Add IFS=$'\n\t' for even safer behavior.

The trap Command

trap lets you execute cleanup code when your script exits or receives a signal. It's essential for cleaning up temp files, releasing locks, etc.

bash
#!/bin/bash

# Create a temp file
tmpfile=$(mktemp)

# Clean up when the script exits (for ANY reason)
trap 'rm -f "$tmpfile"; echo "Cleaned up";' EXIT

# Clean up on Ctrl+C
trap 'echo "Interrupted!"; exit 1' INT

# Your script logic...
echo "Working with $tmpfile"
echo "data" > "$tmpfile"

# trap on ERR โ€” runs on any error (with set -e)
trap 'echo "Error on line $LINENO"' ERR

Debugging with bash -x

The -x flag makes Bash print each command before executing it, prefixed with +. It's invaluable for debugging.

bash
# Run entire script in debug mode
bash -x myscript.sh

# Or enable/disable debugging in parts of your script
set -x     # Turn on debugging
echo "This will be traced"
some_function
set +x     # Turn off debugging
echo "This won't be traced"

# Custom debug output
debug() {
    [[ "${DEBUG:-0}" == "1" ]] && echo "[DEBUG] $*" >&2
}

DEBUG=1
debug "Processing file: $filename"

Error Messages to stderr

Always write error messages to stderr (&2) so they don't mix with normal output.

bash
# Error function
err() {
    echo "[ERROR] $*" >&2
}

warn() {
    echo "[WARN] $*" >&2
}

# Usage
if [[ ! -f "$config_file" ]]; then
    err "Config file not found: $config_file"
    exit 1
fi
โš ๏ธ Warning: If your script writes errors to stdout instead of stderr, they'll be invisible when the output is piped or redirected. Always use >&2 for error messages.

Try It Yourself

Terminal

Summary

You've learned exit codes and $?, the strict mode triplet (set -euo pipefail), how to use trap for cleanup, debugging with bash -x, and the importance of writing errors to stderr. These practices turn fragile scripts into production-quality tools.

๐Ÿงช Test Your Knowledge

Answer the questions below to check your understanding of this lesson.