Error Handling Techniques
Robust scripts anticipate and handle errors gracefully. Learn techniques to make your scripts reliable and user-friendly.
Basic Error Checking
Always check for errors after critical operations:
#!/bin/bash
# Check command success
if ! mkdir -p "$BACKUP_DIR"; then
echo "Error: Could not create backup directory" >&2
exit 1
fi
# Check file exists before reading
if [ ! -f "$CONFIG_FILE" ]; then
echo "Error: Config file not found: $CONFIG_FILE" >&2
exit 1
fi
# Check variable is set
if [ -z "$API_KEY" ]; then
echo "Error: API_KEY is not set" >&2
exit 1
fi
The die Function Pattern
Create a reusable error function:
#!/bin/bash
die() {
echo "Error: $1" >&2
exit "${2:-1}"
}
# Usage
[ -f "$FILE" ] || die "File not found: $FILE" 2
[ -d "$DIR" ] || die "Directory not found: $DIR" 3
Set Options for Safety
#!/bin/bash
# Recommended safety options
set -e # Exit on error
set -u # Error on undefined variable
set -o pipefail # Catch pipe failures
# Combined shorthand
set -euo pipefail
# Your script here
Exercise: Error Function
Create and use a die function:
Trap for Cleanup
trap runs commands when signals are received:
#!/bin/bash
# Cleanup function
cleanup() {
echo "Cleaning up..."
rm -f "$TEMPFILE"
}
# Set trap
trap cleanup EXIT
# Create temp file
TEMPFILE=$(mktemp)
echo "Working with $TEMPFILE"
# If script exits (normally or error), cleanup runs
Common Trap Signals
| Signal | Description |
|---|---|
| EXIT | Script exits (any reason) |
| ERR | Command returns non-zero |
| INT | Interrupt (Ctrl+C) |
| TERM | Terminate signal |
| HUP | Hangup |
#!/bin/bash
trap 'echo "Interrupted!"; exit 1' INT
trap 'echo "Error on line $LINENO"' ERR
trap 'cleanup' EXIT
# Script continues...
Error Context
Provide helpful error messages:
#!/bin/bash
SCRIPT_NAME="${0##*/}"
LOG_FILE="/var/log/$SCRIPT_NAME.log"
log_error() {
local msg="$1"
local line="${2:-unknown}"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR at line $line: $msg" >&2
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR at line $line: $msg" >> "$LOG_FILE"
}
# Usage
if ! some_command; then
log_error "some_command failed" "$LINENO"
exit 1
fi
Try-Catch Pattern
Bash doesn't have try-catch, but we can simulate it:
#!/bin/bash
try() {
"$@"
}
catch() {
return $?
}
# Usage
if ! try risky_command arg1 arg2; then
catch
echo "Command failed with status $?"
# Handle error
fi
Better approach with subshell:
#!/bin/bash
{
# "Try" block
set -e
risky_command1
risky_command2
risky_command3
} || {
# "Catch" block
echo "An error occurred"
exit 1
}
Exercise: Trap Cleanup
Use trap for cleanup:
Validating Input
#!/bin/bash
validate_args() {
if [ $# -lt 2 ]; then
echo "Usage: $0 <source> <dest>" >&2
return 1
fi
local source="$1"
local dest="$2"
if [ ! -f "$source" ]; then
echo "Error: Source file not found: $source" >&2
return 2
fi
if [ ! -d "$(dirname "$dest")" ]; then
echo "Error: Destination directory not found" >&2
return 3
fi
return 0
}
# Main
if ! validate_args "$@"; then
exit $?
fi
Defensive Scripting
#!/bin/bash
set -euo pipefail
# Fail fast on undefined variables
readonly REQUIRED_VAR="${REQUIRED_VAR:?'REQUIRED_VAR must be set'}"
# Safe file operations
readonly SAFE_DIR="/allowed/path"
FILE_PATH=$(realpath -m "$1")
[[ "$FILE_PATH" == "$SAFE_DIR"/* ]] || die "Path outside safe directory"
# Safe arithmetic
[[ "$COUNT" =~ ^[0-9]+$ ]] || die "COUNT must be a number"
# Timeout long operations
timeout 30 long_running_command || die "Command timed out"
Error Handling Template
#!/bin/bash
set -euo pipefail
# Constants
readonly SCRIPT_NAME="${0##*/}"
# Error handling
die() {
echo "[$SCRIPT_NAME] Error: $1" >&2
exit "${2:-1}"
}
cleanup() {
# Cleanup code here
rm -f "$TMPFILE" 2>/dev/null || true
}
trap cleanup EXIT
trap 'die "Interrupted" 130' INT TERM
# Validate
[ $# -ge 1 ] || die "Usage: $SCRIPT_NAME <arg>"
# Main logic
main() {
TMPFILE=$(mktemp) || die "Could not create temp file"
# Your code here
}
main "$@"
Key Takeaways
- Always check return values of critical commands
- Create a
die()function for consistent error handling - Use
set -euo pipefailfor safety - Use
trapfor cleanup on exit or error - Provide context in error messages (line number, values)
- Validate all inputs before processing
- Handle both expected and unexpected errors
- Log errors for debugging

