case matches a value against patterns and runs the first branch that fits. It is bash's switch statement, and it earns its keep the moment a script grows subcommands: deploy.sh start, stop, status — an if-ladder for that reads like alphabet soup; case reads like a table of contents.
The shape #
case "$1" in
start) echo "starting aospix" ;;
stop) echo "stopping aospix" ;;
status) systemctl status aospix ;;
restart) echo "restarting";;
*) echo "usage: $0 {start|stop|status|restart}" >&2; exit 1 ;;
esac
The syntax is case value in pattern) commands ;; branches, closed by esac (case backwards, like if/fi). Patterns use glob syntax — * matches anything, so the final *) is the default branch, and its job is usually the usage message with a non-zero exit code.
Patterns are globs, and they compose #
| Pattern | Matches | |
|---|---|---|
start) | Exactly "start" | |
| `-h | --help)` | Either spelling |
v*) | Anything starting with v | |
*.conf) | Files ending in .conf | |
?????) | Exactly five characters | |
*) | Everything — the default |
Alternation inside a branch is a pipe of patterns, not a regex — same glob family as filename expansion, which is why *.log) needs no quoting worries.
The classic uses #
Parsing a script's own subcommands, as above, is half of all case usage. The other half is classifying values from data:
case "$file" in
*.tar.gz|*.tgz) tar -xzf "$file" ;;
*.zip) unzip "$file" ;;
*.png|*.jpg) echo "image: $file" ;;
*) echo "unknown type: $file" ;;
esac
Combined with a for loop, that pattern processes a whole directory by type — the skeleton of most file-organising scripts.
The details that matter #
;;ends each branch (and bash's;&falls through deliberately,;;&continues testing — rarely needed, good to recognise).- The value should be quoted (
"$1"); patterns should not be. - case does not expand the matched value, so globby input is safe.
Arguments feeding the case come from $1 and getopts; the branches usually end in tidy exit codes. The scripting series continues.
