npx skills add https://github.com/wshobson/agents --skill bash-defensive-patternsHow Bash Defensive Patterns fits into a Paperclip company.
Bash Defensive Patterns drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.
Pre-configured AI company — 18 agents, 18 skills, one-time purchase.
SKILL.md533 linesExpandCollapse
---name: bash-defensive-patternsdescription: Master defensive Bash programming techniques for production-grade scripts. Use when writing robust shell scripts, CI/CD pipelines, or system utilities requiring fault tolerance and safety.--- # Bash Defensive Patterns Comprehensive guidance for writing production-ready Bash scripts using defensive programming techniques, error handling, and safety best practices to prevent common pitfalls and ensure reliability. ## When to Use This Skill - Writing production automation scripts- Building CI/CD pipeline scripts- Creating system administration utilities- Developing error-resilient deployment automation- Writing scripts that must handle edge cases safely- Building maintainable shell script libraries- Implementing comprehensive logging and monitoring- Creating scripts that must work across different platforms ## Core Defensive Principles ### 1. Strict Mode Enable bash strict mode at the start of every script to catch errors early. ```bash#!/bin/bashset -Eeuo pipefail # Exit on error, unset variables, pipe failures``` **Key flags:** - `set -E`: Inherit ERR trap in functions- `set -e`: Exit on any error (command returns non-zero)- `set -u`: Exit on undefined variable reference- `set -o pipefail`: Pipe fails if any command fails (not just last) ### 2. Error Trapping and Cleanup Implement proper cleanup on script exit or error. ```bash#!/bin/bashset -Eeuo pipefail trap 'echo "Error on line $LINENO"' ERRtrap 'echo "Cleaning up..."; rm -rf "$TMPDIR"' EXIT TMPDIR=$(mktemp -d)# Script code here``` ### 3. Variable Safety Always quote variables to prevent word splitting and globbing issues. ```bash# Wrong - unsafecp $source $dest # Correct - safecp "$source" "$dest" # Required variables - fail with message if unset: "${REQUIRED_VAR:?REQUIRED_VAR is not set}"``` ### 4. Array Handling Use arrays safely for complex data handling. ```bash# Safe array iterationdeclare -a items=("item 1" "item 2" "item 3") for item in "${items[@]}"; do echo "Processing: $item"done # Reading output into array safelymapfile -t lines < <(some_command)readarray -t numbers < <(seq 1 10)``` ### 5. Conditional Safety Use `[[ ]]` for Bash-specific features, `[ ]` for POSIX. ```bash# Bash - saferif [[ -f "$file" && -r "$file" ]]; then content=$(<"$file")fi # POSIX - portableif [ -f "$file" ] && [ -r "$file" ]; then content=$(cat "$file")fi # Test for existence before operationsif [[ -z "${VAR:-}" ]]; then echo "VAR is not set or is empty"fi``` ## Fundamental Patterns ### Pattern 1: Safe Script Directory Detection ```bash#!/bin/bashset -Eeuo pipefail # Correctly determine script directorySCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"SCRIPT_NAME="$(basename -- "${BASH_SOURCE[0]}")" echo "Script location: $SCRIPT_DIR/$SCRIPT_NAME"``` ### Pattern 2: Comprehensive Function Templat ```bash#!/bin/bashset -Eeuo pipefail # Prefix for functions: handle_*, process_*, check_*, validate_*# Include documentation and error handling validate_file() { local -r file="$1" local -r message="${2:-File not found: $file}" if [[ ! -f "$file" ]]; then echo "ERROR: $message" >&2 return 1 fi return 0} process_files() { local -r input_dir="$1" local -r output_dir="$2" # Validate inputs [[ -d "$input_dir" ]] || { echo "ERROR: input_dir not a directory" >&2; return 1; } # Create output directory if needed mkdir -p "$output_dir" || { echo "ERROR: Cannot create output_dir" >&2; return 1; } # Process files safely while IFS= read -r -d '' file; do echo "Processing: $file" # Do work done < <(find "$input_dir" -maxdepth 1 -type f -print0) return 0}``` ### Pattern 3: Safe Temporary File Handling ```bash#!/bin/bashset -Eeuo pipefail trap 'rm -rf -- "$TMPDIR"' EXIT # Create temporary directoryTMPDIR=$(mktemp -d) || { echo "ERROR: Failed to create temp directory" >&2; exit 1; } # Create temporary files in directoryTMPFILE1="$TMPDIR/temp1.txt"TMPFILE2="$TMPDIR/temp2.txt" # Use temporary filestouch "$TMPFILE1" "$TMPFILE2" echo "Temp files created in: $TMPDIR"``` ### Pattern 4: Robust Argument Parsing ```bash#!/bin/bashset -Eeuo pipefail # Default valuesVERBOSE=falseDRY_RUN=falseOUTPUT_FILE=""THREADS=4 usage() { cat <<EOFUsage: $0 [OPTIONS] Options: -v, --verbose Enable verbose output -d, --dry-run Run without making changes -o, --output FILE Output file path -j, --jobs NUM Number of parallel jobs -h, --help Show this help messageEOF exit "${1:-0}"} # Parse argumentswhile [[ $# -gt 0 ]]; do case "$1" in -v|--verbose) VERBOSE=true shift ;; -d|--dry-run) DRY_RUN=true shift ;; -o|--output) OUTPUT_FILE="$2" shift 2 ;; -j|--jobs) THREADS="$2" shift 2 ;; -h|--help) usage 0 ;; --) shift break ;; *) echo "ERROR: Unknown option: $1" >&2 usage 1 ;; esacdone # Validate required arguments[[ -n "$OUTPUT_FILE" ]] || { echo "ERROR: -o/--output is required" >&2; usage 1; }``` ### Pattern 5: Structured Logging ```bash#!/bin/bashset -Eeuo pipefail # Logging functionslog_info() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] INFO: $*" >&2} log_warn() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] WARN: $*" >&2} log_error() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $*" >&2} log_debug() { if [[ "${DEBUG:-0}" == "1" ]]; then echo "[$(date +'%Y-%m-%d %H:%M:%S')] DEBUG: $*" >&2 fi} # Usagelog_info "Starting script"log_debug "Debug information"log_warn "Warning message"log_error "Error occurred"``` ### Pattern 6: Process Orchestration with Signals ```bash#!/bin/bashset -Eeuo pipefail # Track background processesPIDS=() cleanup() { log_info "Shutting down..." # Terminate all background processes for pid in "${PIDS[@]}"; do if kill -0 "$pid" 2>/dev/null; then kill -TERM "$pid" 2>/dev/null || true fi done # Wait for graceful shutdown for pid in "${PIDS[@]}"; do wait "$pid" 2>/dev/null || true done} trap cleanup SIGTERM SIGINT # Start background tasksbackground_task &PIDS+=($!) another_task &PIDS+=($!) # Wait for all background processeswait``` ### Pattern 7: Safe File Operations ```bash#!/bin/bashset -Eeuo pipefail # Use -i flag to move safely without overwritingsafe_move() { local -r source="$1" local -r dest="$2" if [[ ! -e "$source" ]]; then echo "ERROR: Source does not exist: $source" >&2 return 1 fi if [[ -e "$dest" ]]; then echo "ERROR: Destination already exists: $dest" >&2 return 1 fi mv "$source" "$dest"} # Safe directory cleanupsafe_rmdir() { local -r dir="$1" if [[ ! -d "$dir" ]]; then echo "ERROR: Not a directory: $dir" >&2 return 1 fi # Use -I flag to prompt before rm (BSD/GNU compatible) rm -rI -- "$dir"} # Atomic file writesatomic_write() { local -r target="$1" local -r tmpfile tmpfile=$(mktemp) || return 1 # Write to temp file first cat > "$tmpfile" # Atomic rename mv "$tmpfile" "$target"}``` ### Pattern 8: Idempotent Script Design ```bash#!/bin/bashset -Eeuo pipefail # Check if resource already existsensure_directory() { local -r dir="$1" if [[ -d "$dir" ]]; then log_info "Directory already exists: $dir" return 0 fi mkdir -p "$dir" || { log_error "Failed to create directory: $dir" return 1 } log_info "Created directory: $dir"} # Ensure configuration stateensure_config() { local -r config_file="$1" local -r default_value="$2" if [[ ! -f "$config_file" ]]; then echo "$default_value" > "$config_file" log_info "Created config: $config_file" fi} # Rerunning script multiple times should be safeensure_directory "/var/cache/myapp"ensure_config "/etc/myapp/config" "DEBUG=false"``` ### Pattern 9: Safe Command Substitution ```bash#!/bin/bashset -Eeuo pipefail # Use $() instead of backticksname=$(<"$file") # Modern, safe variable assignment from fileoutput=$(command -v python3) # Get command location safely # Handle command substitution with error checkingresult=$(command -v node) || { log_error "node command not found" return 1} # For multiple linesmapfile -t lines < <(grep "pattern" "$file") # NUL-safe iterationwhile IFS= read -r -d '' file; do echo "Processing: $file"done < <(find /path -type f -print0)``` ### Pattern 10: Dry-Run Support ```bash#!/bin/bashset -Eeuo pipefail DRY_RUN="${DRY_RUN:-false}" run_cmd() { if [[ "$DRY_RUN" == "true" ]]; then echo "[DRY RUN] Would execute: $*" return 0 fi "$@"} # Usagerun_cmd cp "$source" "$dest"run_cmd rm "$file"run_cmd chown "$owner" "$target"``` ## Advanced Defensive Techniques ### Named Parameters Pattern ```bash#!/bin/bashset -Eeuo pipefail process_data() { local input_file="" local output_dir="" local format="json" # Parse named parameters while [[ $# -gt 0 ]]; do case "$1" in --input=*) input_file="${1#*=}" ;; --output=*) output_dir="${1#*=}" ;; --format=*) format="${1#*=}" ;; *) echo "ERROR: Unknown parameter: $1" >&2 return 1 ;; esac shift done # Validate required parameters [[ -n "$input_file" ]] || { echo "ERROR: --input is required" >&2; return 1; } [[ -n "$output_dir" ]] || { echo "ERROR: --output is required" >&2; return 1; }}``` ### Dependency Checking ```bash#!/bin/bashset -Eeuo pipefail check_dependencies() { local -a missing_deps=() local -a required=("jq" "curl" "git") for cmd in "${required[@]}"; do if ! command -v "$cmd" &>/dev/null; then missing_deps+=("$cmd") fi done if [[ ${#missing_deps[@]} -gt 0 ]]; then echo "ERROR: Missing required commands: ${missing_deps[*]}" >&2 return 1 fi} check_dependencies``` ## Best Practices Summary 1. **Always use strict mode** - `set -Eeuo pipefail`2. **Quote all variables** - `"$variable"` prevents word splitting3. **Use [[]] conditionals** - More robust than [ ]4. **Implement error trapping** - Catch and handle errors gracefully5. **Validate all inputs** - Check file existence, permissions, formats6. **Use functions for reusability** - Prefix with meaningful names7. **Implement structured logging** - Include timestamps and levels8. **Support dry-run mode** - Allow users to preview changes9. **Handle temporary files safely** - Use mktemp, cleanup with trap10. **Design for idempotency** - Scripts should be safe to rerun11. **Document requirements** - List dependencies and minimum versions12. **Test error paths** - Ensure error handling works correctly13. **Use `command -v`** - Safer than `which` for checking executables14. **Prefer printf over echo** - More predictable across systemsAccessibility Compliance
This walks you through implementing proper WCAG 2.2 compliance with real code patterns for screen readers, keyboard navigation, and mobile accessibility. It cov
Airflow Dag Patterns
If you're building data pipelines with Airflow, this skill gives you production-ready DAG patterns that actually work in the real world. It covers TaskFlow API
Angular Migration
Migrating from AngularJS to Angular is notoriously painful, and this skill tackles the practical stuff that makes or breaks these projects. It covers hybrid app