Bash Automation Patterns
Bash is powerful because it combines commands, files, streams, and text. It is dangerous for the same reason. Small quoting or expansion mistakes can turn one file into many, hide errors, or send output to the wrong place.
The scripts that hurt teams are rarely the long ones. They are the short ones that "obviously work" until they run unattended at 2am.
Start With A Shebang And Exit Strategy
Use a clear interpreter:
#!/bin/bash
Return 0 for success and non-zero for failure. Scripts used by cron, CI, or automation systems should make failure visible.
That sounds basic, but a lot of fragile automation is really just a chain of commands with no deliberate failure contract.
Quote Expansions
This is the most important habit:
touch "$filename"
Without quotes, Bash performs word splitting and globbing. A variable containing spaces can become multiple arguments. A variable containing * can match files.
Use "$@" when forwarding arguments. It preserves each argument as a separate word.
This one rule prevents an absurd amount of damage. If a script has only one defensive habit, it should be quoting expansions.
Know Expansion Order
Bash expands in stages:
- Brace expansion.
- Parameter, arithmetic, command, and tilde expansion.
- Word splitting.
- Globbing.
Most surprising behavior comes from forgetting steps 3 and 4. A script that looks correct in your head can still explode because the shell is doing more interpretation than you think.
Redirection
Useful descriptors:
0: stdin.1: stdout.2: stderr.&>: stdout and stderr.
When debugging automation, keep stderr visible unless you intentionally handle it. Hiding errors with 2>/dev/null can make scripts look successful while they are failing.
If you need both output capture and visibility, prefer explicit logging with tee or structured redirection instead of blanket suppression.
Safer Modes
For many scripts:
set -euo pipefail
This exits on errors, treats unset variables as failures, and catches failures inside pipelines. Use it thoughtfully because some commands intentionally return non-zero.
set -euo pipefail is not a badge of seriousness. It is a bias toward surfacing failure early. You still need to know when a command is expected to return non-zero and handle that case explicitly.
Trap Cleanup
Use trap for cleanup:
tmpdir="$(mktemp -d)" trap 'rm -rf "$tmpdir"' EXIT
This keeps temporary files from accumulating and makes scripts safer to interrupt.
trap is also one of the easiest ways to make automation feel professional instead of disposable.
Cron Mindset
Cron runs with a smaller environment than your interactive shell. Always use absolute paths or set PATH explicitly. Redirect output to logs. Test the command outside cron first, then test it under cron-like environment assumptions.
Also choose the scheduler intentionally:
atfor one-off execution.cronfor periodic schedules on always-on hosts.anacronwhen the machine may be powered off and missed jobs must catch up later.
If you use run-parts, remember its naming rules. Many cron-directory bugs come from scripts that are executable but ignored because the file name does not meet the runner's expectations.
Practical Rule
Write Bash as if another process will run it at 2am with no terminal, no aliases, a small environment, and only logs for evidence. That mindset produces better scripts.