BASH / CHAPTER 12
Build predictable command-line tools
AdvancedSpecify arguments, cleanup, and observable outcomes.
By the end of this chapter
- Validate options and positional inputs before work
- Keep external data out of shell source
- Use explicit status handling and cleanup policies
The idea
A dependable Bash tool begins with a command-line contract: accepted options, required arguments, output format, and exit statuses. Parse options with a documented mechanism such as getopts, then check the remaining positional arguments before doing work. Preserve arguments with arrays and quoting. Treat data as arguments, never as shell source for eval, especially when values contain characters that have shell meaning.
Failure handling needs an explicit policy. Check operations whose success is required, preserve a failing status before issuing diagnostics, and arrange resource cleanup with a trap when the tool owns temporary resources. The errexit option has contextual exceptions and is not a complete error-handling design. Separate a reusable function from its command-line wrapper, making ordinary results and invalid invocations straightforward to test without performing irreversible actions.
#!/usr/bin/env bash
render() {
if (( $# != 2 )); then printf 'usage: render prefix label\n' >&2; return 2; fi
local prefix=$1 label=$2
printf '%s: %s\n' "$prefix" "$label"
}
render "Ready" "alpha beta"Ready: alpha beta
The function validates arity before accessing arguments, keeps state local, and prints through a fixed format without interpreting label content as code.
Try it yourself
Design a CLI accepting an optional prefix and any number of labels, with a usage error for a missing option argument.
Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.
CHECK YOUR UNDERSTANDING