# Bash Scripting

> When to activate: bash, shell script, sh, set -e, trap, argument parsing, heredoc, parallel, cron script, automation

- Skill: `mattakushi432/bash-scripting` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mattakushi432/bash-scripting`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mattakushi432/bash-scripting/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Mattakushi432 (https://skillmd.com/u/mattakushi432)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mattakushi432/bash-scripting

---

# Bash Scripting Patterns

## Script Header (always use)

```bash
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'

readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"
```

## Error Handling with trap

```bash
cleanup() {
  local exit_code=$?
  echo "[${SCRIPT_NAME}] Cleaning up (exit: ${exit_code})" >&2
  rm -f /tmp/myapp-lock
  exit "${exit_code}"
}
trap cleanup EXIT INT TERM

die() {
  echo "[ERROR] $*" >&2
  exit 1
}

[[ -f config.yaml ]] || die "config.yaml not found"
```

## Argument Parsing

```bash
usage() {
  cat <<EOF
Usage: ${SCRIPT_NAME} [OPTIONS]

Options:
  -e, --env ENV       Environment (dev|staging|prod)  [required]
  -t, --tag TAG       Docker image tag                [default: latest]
  -d, --dry-run       Print commands, don't execute
  -h, --help          Show this help
EOF
}

ENV=""
TAG="latest"
DRY_RUN=false

while [[ $# -gt 0 ]]; do
  case $1 in
    -e|--env)    ENV="$2";  shift 2 ;;
    -t|--tag)    TAG="$2";  shift 2 ;;
    -d|--dry-run) DRY_RUN=true; shift ;;
    -h|--help)   usage; exit 0 ;;
    *)           die "Unknown option: $1" ;;
  esac
done

[[ -n "${ENV}" ]] || die "--env is required"
[[ "${ENV}" =~ ^(dev|staging|prod)$ ]] || die "Invalid env: ${ENV}"
```

## Run or Dry-Run Helper

```bash
run() {
  echo "+ $*" >&2
  if [[ "${DRY_RUN}" == "false" ]]; then
    "$@"
  fi
}

run kubectl apply -f deployment.yaml
run helm upgrade myapp ./chart --atomic
```

## Retry with Backoff

```bash
retry() {
  local max_attempts=$1; shift
  local delay=5
  local attempt=1
  until "$@"; do
    if (( attempt >= max_attempts )); then
      echo "[ERROR] Command failed after ${max_attempts} attempts: $*" >&2
      return 1
    fi
    echo "[WARN] Attempt ${attempt}/${max_attempts} failed. Retrying in ${delay}s..." >&2
    sleep "${delay}"
    (( attempt++ ))
    (( delay = delay * 2 ))
  done
}

retry 5 curl -sf https://api.example.com/health
```

## Parallel Execution

```bash
# Run jobs in parallel, wait for all, capture failures
pids=()
for region in us-east-1 eu-west-1 ap-southeast-1; do
  ./deploy.sh --region "${region}" &
  pids+=($!)
done

failed=0
for pid in "${pids[@]}"; do
  if ! wait "${pid}"; then
    echo "[ERROR] Job ${pid} failed" >&2
    (( failed++ ))
  fi
done
(( failed == 0 )) || die "${failed} deployment(s) failed"
```

## Heredoc for Config

```bash
cat > /etc/myapp/config.yaml <<EOF
environment: ${ENV}
database:
  host: ${DB_HOST:-localhost}
  port: ${DB_PORT:-5432}
  name: myapp_${ENV}
log_level: ${LOG_LEVEL:-info}
EOF
```

## Logging Functions

```bash
info()  { echo "[$(date -u +%T)] [INFO]  $*"; }
warn()  { echo "[$(date -u +%T)] [WARN]  $*" >&2; }
error() { echo "[$(date -u +%T)] [ERROR] $*" >&2; }

info "Starting deployment to ${ENV}"
warn "This will restart the service"
```

## Key Rules
- `set -euo pipefail` on every script — `e` exits on error, `u` catches unset vars, `o pipefail` catches pipe failures
- Quote every variable: `"${VAR}"` not `$VAR` — prevents word splitting on spaces
- Use `[[ ]]` not `[ ]` — safer string comparisons, no word splitting
- Prefer `printf` over `echo` for portable output
- ShellCheck every script: `shellcheck myscript.sh`

