bash

malston's avatarfrom malston

Defensive Bash scripting patterns for reliable automation. Use when writing shell scripts.

1stars🔀0forks📁View on GitHub🕐Updated Jan 7, 2026

When & Why to Use This Skill

This Claude skill provides a comprehensive framework for defensive Bash scripting, designed to transform fragile shell scripts into robust, production-ready automation tools. It focuses on implementing strict execution modes, advanced error handling, and variable safety patterns to ensure reliability in DevOps and system administration tasks.

Use Cases

  • Developing resilient CI/CD pipeline scripts that fail fast and provide clear diagnostic logs upon error.
  • Creating idempotent system maintenance scripts that safely handle file operations and environment dependencies.
  • Standardizing internal CLI tools with consistent argument parsing, logging, and help documentation.
  • Implementing safety guards in infrastructure-as-code scripts to prevent catastrophic commands like 'rm -rf' on undefined paths.
  • Building automated cleanup routines using traps to ensure temporary resources are removed regardless of script success or failure.
namebash
descriptionDefensive Bash scripting patterns for reliable automation. Use when writing shell scripts.

Bash

Strict Mode

#!/usr/bin/env bash
set -Eeuo pipefail

# -E: ERR traps inherited by functions
# -e: exit on error
# -u: error on undefined variables
# -o pipefail: pipe fails if any command fails

Error Handling

# Trap for cleanup
cleanup() {
    rm -rf "$TEMP_DIR"
}
trap cleanup EXIT

# Error handler with line number
error_handler() {
    echo "Error on line $1" >&2
    exit 1
}
trap 'error_handler $LINENO' ERR

# Check command exists
require_cmd() {
    command -v "$1" >/dev/null 2>&1 || {
        echo "Required command not found: $1" >&2
        exit 1
    }
}
require_cmd jq
require_cmd curl

Variable Safety

# Required variable with error message
: "${REQUIRED_VAR:?Error: REQUIRED_VAR not set}"

# Default value
: "${OPTIONAL_VAR:=default_value}"

# Always quote variables
echo "$FILE_PATH"
rm -rf "${DIR:?}/"  # Prevents rm -rf /

Argument Parsing

usage() {
    cat <<EOF
Usage: $(basename "$0") [OPTIONS] <arg>

Options:
    -h, --help      Show this help
    -v, --verbose   Verbose output
    -f, --file      Input file
EOF
    exit 1
}

VERBOSE=false
FILE=""

while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help) usage ;;
        -v|--verbose) VERBOSE=true; shift ;;
        -f|--file) FILE="$2"; shift 2 ;;
        -*) echo "Unknown option: $1" >&2; usage ;;
        *) ARGS+=("$1"); shift ;;
    esac
done

Logging

readonly LOG_FILE="/var/log/script.log"

log() {
    local level="$1"; shift
    echo "[$(date -Iseconds)] [$level] $*" | tee -a "$LOG_FILE"
}

info()  { log INFO "$@"; }
warn()  { log WARN "$@"; }
error() { log ERROR "$@" >&2; }
debug() { [[ "${DEBUG:-false}" == "true" ]] && log DEBUG "$@"; }

Idempotency

# Check before create
[[ -d "$DIR" ]] || mkdir -p "$DIR"

# Check before download
[[ -f "$FILE" ]] || curl -o "$FILE" "$URL"

# Atomic file write
tmp=$(mktemp)
generate_config > "$tmp"
mv "$tmp" "$CONFIG_FILE"

Best Practices

  • Always use #!/usr/bin/env bash
  • Quote all variables: "$var"
  • Use [[ over [ for conditionals
  • Use $(command) over backticks
  • Use readonly for constants
  • Use local in functions
  • Prefer absolute paths
  • Use mktemp for temp files
  • Run shellcheck on all scripts