Skip to content

Lesson 3 of 9 · 20m

Shell Scripting: When Bash Is Enough

Shell scripts that don't embarrass you — strict mode, quoting, exit codes, and when to reach for Python instead.

Bash is a language for gluing other programs together — reach for it when the job is "run these commands in order" and reach for a real language when the job is "process this data."

Strict mode first

#!/usr/bin/env bash
set -euo pipefail
IFS=$'
	'
  • -e — exit on first failure (no silently continuing on a broken pipe).
  • -u — unset variables are errors, not empty strings.
  • -o pipefail — a pipeline fails if any stage fails, not just the last.
  • IFS — word-splitting on newline/tab, not spaces (spaces in filenames work).

Quoting is a discipline

for f in *.txt; do echo "$f"; done   # "$f" — quoted, preserves spaces
for f in *.txt; do echo $f; done     # $f  — unquoted, word-splits — usually a bug

$var without quotes splits on whitespace and glob-expands — file="my doc.txt"; rm $file deletes my and doc.txt. Always "$var".

The script structure that works

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

usage() { echo "Usage: $0 <environment>"; exit 1; }
[ $# -lt 1 ] && usage
ENV=$1

main() {
  case "$ENV" in
    staging|prod) echo "deploying to $ENV"; deploy "$ENV" ;;
    *) echo "unknown env"; usage ;;
  esac
}

main "$@"

Functions, a main, argument validation, a usage function — the script is readable and testable.

When to stop using bash

  • String manipulation beyond simple substitution → Python/awk.
  • JSON parsing → jq, not regex.
  • Complex data structures → a real language.
  • Anything where error handling matters → Python's try/except beats || echo "failed".

::exercise(strict-mode)

Field notes

  • shellcheck is a linter for shell scripts — it catches quoting bugs, cd without cd || exit, and for over ls. Run it before committing any script.
  • trap 'echo "interrupted"; exit 130' INT — cleanup on Ctrl+C; the script that doesn't trap leaves partial state behind.
  • command -v docker &>/dev/null || { echo "install docker"; exit 1; } — dependency check pattern.