Part 3 · 1 chapters · ~8 min

Shell Scripting Properly

Strict mode (set -euo pipefail) and its caveats, quoting and word splitting, arrays, parameter expansion and defaults, conditionals and [[ ]], functions and local variables, traps and cleanup, temporary files, argument parsing, shellcheck in CI, portability (bash versus POSIX sh versus zsh), and when to switch to a real language.

4

A script template

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

usage() { echo "usage: $0 -e <env> <release-tag>" >&2; exit 2; }
env=""; while getopts "e:" opt; do case $opt in e) env=$OPTARG ;; *) usage ;; esac; done
shift $((OPTIND - 1)); tag=${1:?release tag required}
[[ $env =~ ^(staging|production)$ ]] || usage

tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT          # always clean up, even on error
files=( "$tmp"/*.json )                               # arrays, not word-split strings
log() { printf '%s %s\n' "$(date -u +%FT%TZ)" "$*" >&2; }

log "deploying $tag to $env"
curl -fsS "https://releases.example.com/$tag/manifest.json" -o "$tmp/manifest.json"   # -f: fail on HTTP errors
jq -e '.digest' "$tmp/manifest.json" > /dev/null       # -e: non-zero exit if missing
# shellcheck deploy.sh   → run in CI

Caveat: set -e does not trigger inside if conditions, in commands joined with && or ||, or in some subshells; check critical commands explicitly. Write macOS-and-Linux scripts for bash (macOS ships bash 3.2 and uses zsh as the default login shell) or plain POSIX sh.

SHELL SCRIPTS THAT FAIL LOUDLY
the defaults are dangerous; fix them at the top
set -eExit when a command fails (withknown exceptions insideconditions).set -uError on unset variables: nosilent rm -rf "$DIR/" with emptyDIR.set -o pipefailA pipeline fails if any stagefails, not just the last.quote everything"$var" not $var: spaces and globsbreak unquoted expansions.trapClean up temp files on exit, erroror Ctrl-C.shellcheckA linter that catches most of theabove automatically.
swipe the figure sideways, or tap expand for full screen
1/4
strict mode
Without set -euo pipefail, a failed command is ignored and the script continues with bad state; a failed curl piped into jq looks like success.
set -euo pipefailfail loudly