100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Bash

Case Statements in Depth

Learn how Bash's case statement uses glob patterns, fall-through terminators, and pattern lists to replace long if/elif chains.

Control FlowIntermediate8 min readJul 10, 2026
Analogies

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.

bash
#!/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
        ;;
esac

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

  • case matches 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.
  • case is the idiomatic tool for dispatching subcommands (start, stop, status) and parsing keyword flags.
  • Patterns in case are shell globs, not regex — use [[ =~ ]] when true regex power is needed.
  • case evaluates its subject once, making it clearer and slightly faster than an equivalent if/elif chain.

Practice what you learned

Was this page helpful?

Topics covered

#Bash#AdvancedBashScriptingStudyNotes#DevOps#CaseStatementsInDepth#Case#Statements#Depth#Beats#StudyNotes#SkillVeris