Why case Beats a Long if/elif Chain
The case statement matches a single value against a series of glob patterns and executes the block for the first match, which makes it far more readable than a long chain of if [[ $x == a ]]; elif [[ $x == b ]]; comparisons once you have more than two or three branches. Each pattern is a shell glob, not a regex, so * matches any sequence, ? matches a single character, and [abc] matches a character class, all evaluated without invoking [[ ]] or test at all. Because case only evaluates its subject expression once, it's also slightly more efficient and less error-prone than repeated string comparisons against the same variable.
Cricket analogy: A case statement is like an umpire's decision tree for dismissals — bowled, caught, lbw, run out — checked in order against one delivery outcome, instead of a commentator re-describing the ball four separate times.
Pattern Lists and Fall-Through Terminators
A single case branch can match multiple patterns separated by |, as in y|Y|yes) ...;;, avoiding repeated blocks for equivalent inputs. Bash also supports three different terminators after a branch's commands: ;; stops at the first match, ;& falls through unconditionally into the next branch's commands regardless of whether its pattern matches, and ;;& continues testing subsequent patterns and executes any that also match, rather than stopping. ;& and ;;& are Bash extensions not available in plain POSIX sh, so scripts requiring portability should stick to ;; and combine patterns with | instead.
Cricket analogy: A pattern list like w|W|wicket) matching several spellings of the same dismissal type is like a scorer accepting 'W', 'wkt', or 'out' as equivalent entries for the same event on the scoresheet.
Real-World Uses: Argument Parsing and Dispatch Tables
case is the idiomatic way to parse single-character or keyword command-line flags in a while getopts loop, and it's equally common as a dispatch mechanism where a subcommand string like start, stop, or restart selects which function to call, mimicking how tools like systemctl or git route subcommands internally. A default *) branch at the end acts as a catch-all for unrecognized input, which is essential for producing a helpful usage error instead of silently doing nothing when a user mistypes a flag or subcommand.
Cricket analogy: A dispatch-table case for subcommands like start|stop|status) mirrors how a match official routes an appeal to exactly one protocol — DRS review, third umpire, or on-field call — based on the type of appeal raised.
#!/usr/bin/env bash
set -euo pipefail
subcommand=${1:-help}
shift || true
case "$subcommand" in
start|up)
echo "Starting service..."
;;
stop|down)
echo "Stopping service..."
;;
restart)
echo "Restarting: stopping then starting."
;;&
status)
echo "Checking status..."
;;
-h|--help|help)
cat <<EOF
Usage: $0 {start|stop|restart|status}
EOF
;;
*)
echo "Unknown subcommand: $subcommand" >&2
exit 1
;;
esaccase patterns are shell globs, not regular expressions. If you need true regex matching (character classes with quantifiers, anchors, backreferences), use [[ $var =~ regex ]] instead — case cannot express [0-9]{3} style quantifiers.
casematches a value against ordered glob patterns and runs the first (or, with;;&, every) matching branch.- Combine equivalent inputs into one branch with
|, e.g.y|Y|yes). ;;stops after a match;;&falls through unconditionally;;;&re-tests remaining patterns — the latter two are Bash-only.- Always include a
*)default branch to catch unrecognized input and fail with a clear error. caseis the idiomatic tool for dispatching subcommands (start,stop,status) and parsing keyword flags.- Patterns in
caseare shell globs, not regex — use[[ =~ ]]when true regex power is needed. caseevaluates its subject once, making it clearer and slightly faster than an equivalent if/elif chain.
Practice what you learned
1. What kind of pattern matching does `case` use by default?
2. Which terminator causes execution to fall through into the next branch's commands unconditionally?
3. How do you make one `case` branch match several equivalent inputs like 'y', 'Y', and 'yes'?
4. Why is a trailing `*)` branch considered best practice in a case statement?
5. Is `;;&` available in POSIX sh?
Was this page helpful?
You May Also Like
Advanced Conditionals and Test Operators
Master Bash's test, [ ], and [[ ]] constructs with string, numeric, and file test operators to write robust, bug-free conditional logic.
Advanced Loop Patterns
Go beyond basic for loops with C-style iteration, while-read idioms for safe line processing, and loop control with break, continue, and select.
Command Substitution in Depth
Understand how $(...) captures command output as a string, why it's preferred over backticks, and the quoting rules that prevent word-splitting bugs.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsDocker & Kubernetes Study Notes
YAML · 40 topics
DevOpsCI/CD Tools & Pipelines Study Notes
YAML · 37 topics