ArcLibrary

Shell Scripting & Debugging

Shebang, variables, loops, error handling — write a bash script that won't bite you.

ShellbashScripts
核心 · Key Idea

In one line: bash scripts are quick to write but full of footguns. The trio set -euo pipefail + always-quote variables + shellcheck eliminates 90 % of hidden bugs.

What it is#

Minimal viable script:

#!/usr/bin/env bash
set -euo pipefail
 
NAME="${1:-world}"
echo "Hello, $NAME"
 
for f in *.log; do
  echo "processing $f"
  gzip "$f"
done

#!/usr/bin/env bash is more portable than #!/bin/bash (uses PATH).

Analogy#

打个比方 · Analogy

bash is glue language — it sticks small tools together. Don't use it for complex logic — once a script crosses 100 lines, switch to Python / Go.

Key concepts#

shebang#!
First line tells the kernel which interpreter to use.
set -eerrexit
Exit immediately on any non-zero return.
set -unounset
Error on unset variables (prevents `rm -rf $UNDEFINED/`).
set -o pipefailpipefail
Any failure in a pipeline fails the whole pipeline (default only checks the last command).
trapSignal / exit hook
`trap cleanup EXIT` — clean up temp files before the script exits.
$1 / $@ / $#Positional args
$1 first arg; $@ all; $# count.
$(cmd)Command substitution
Embeds cmd output into a string (clearer than backticks).

Common template#

#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
 
log() { echo "[$(date +%T)] $*" >&2; }
 
cleanup() {
  rm -f "$tmp"
}
trap cleanup EXIT
 
tmp=$(mktemp)
log "starting"
 
# Bail loudly when required env vars are missing:
: "${API_KEY:?need API_KEY}"
 
# Safe quoting — paths with spaces are fine
for f in "$dir"/*.log; do
  [[ -e "$f" ]] || continue
  gzip "$f"
done
 
log "done"

Debugging recipe#

shellcheck is mandatory — catches unquoted vars, wrong ==, [ ] vs [[ ]] issues, etc.

Practical notes#

  • Always quote variables: "$var" — strings with spaces / globs won't split into multiple args.
  • Prefer [[ ]] over [ ]: modern bash test, safe even without quotes.
  • Use ${var:-default} / ${var:?msg} for fallback / required-arg checks.
  • Don't parse ls — use for f in * or find ... -print0 | xargs -0.
  • Don't ignore errors: cmd || die "msg", if ! cmd; then ... fi.
  • Temp files via mktemp — avoids race conditions and shared /tmp collisions.
  • Past 100 lines → Python. Picking bash for complex logic is usually the wrong language choice.

Easy confusions#

bash script
File + shebang + `set -euo pipefail`.
Reusable, CI-friendly.
ad-hoc command
One-shot pipeline `cmd1 | cmd2 | cmd3`.
Fine for debugging — **don't paste it into prod**.

Further reading#