BASH / CHAPTER 12

Build predictable command-line tools

Advanced

Specify 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.

Bash example
#!/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"
Result
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

Chapter checkpoint

Which design safely handles a label that contains shell metacharacters?