Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Bash — House Rules

Who this is for — anyone writing a canary, a build script, or any glue in these repositories. Reference material: grep it, don’t read it.

Read this if — you are about to type [[.
Skip this if — you want the reasoning behind the subset; that is How to Read the House Rules.

Takeaways

  1. set -euo pipefail, always, on line two.
  2. if test … — never [ … ], never [[ … ]], and then/else/do on their own lines.
  3. Quote every expansion; assume the default awk is mawk.

Bash is an incredibly powerful glue language when restricted to its robust, readable features. Forget old habits and legacy scripts. This is the subset we write.


1. The Strict Header

Always start script execution with:

#!/usr/bin/env bash
set -euo pipefail
  • -e: Exit immediately if any command returns a non-zero status.
  • -u: Exit if an uninitialized variable is referenced.
  • -o pipefail: Ensure that pipeline return codes reflect the last non-zero command in the chain, not just the final command.

2. Conditionals: Use test Command

Never use the brackets [ ... ] or [[ ... ]]. Instead, use the clean and standard test command.

if test -f "$file"
then printf 'Found %s\n' "$file"
fi

if test "$a" = "$b"
then printf 'Equal\n'
else printf 'Not equal\n'
fi
  • Put then / else / do always on their own lines.
  • Avoid semicolons where possible.

3. Variable Safety

  • Always double-quote variables when accessing: "$var" or "${var}".
  • Handle required arguments with default error patterns: pattern="${1:?Usage: script.sh PATTERN}"
  • Use local inside functions. For command substitutions, declare first, then assign, to prevent local from masking execution exit codes:
    local result
    result=$(command args)
    

4. Alignment Continuation

Do not follow rigid N-space indents. Instead, use alignment continuation to make long command pipelines and logic structures readable:

# Line up commands under the start of the chain
result=$(some_command |
         grep "pattern" |
         awk '{print $2}')

# Line up conditions under the first test
if test -f "$a" &&
   test -d "$b"
then stmt1
     stmt2
fi

5. Output discipline

  • Use printf instead of echo for printing variables: printf '%s\n' "$var".

  • Write helper functions to handle pass/fail messages and stderr logging:

    pass() {
       printf '  ✓ %s\n' "$*"
    }
    
    fail() {
       printf 'ERROR: %s\n' "$*" >&2
       exit 1
    }
    

    This is the shape every script under scripts/ uses — the page and the repository agree, which is the only reason to trust either.

6. Awk Portability (Warning)

On standard Debian, Ubuntu, and Raspberry Pi OS, the default awk is mawk, not gawk. Avoid gawk extensions:

  • ❌ Do not use 3-argument match().
  • ❌ Do not use strtonum().
  • ❌ Do not use gensub(). Use standard portable constructs like split(), sub(), and gsub().