Quicklinks | Read This! | Picolisp | Picolisp Machine | Pil Sources | Pil Tutorials | Linux | BASH | C-Programmming | Javascipt | Python | Scheme | Operating Systems | AssemblyLanguage | Computer Security | Firewalls | Exploitation | Social Engineering | Metasploit | Emacs | vim | Pharo Smalltalk | Databases | Networking | Machine Learning | Git | Machine Learning | Algorithms | Open Data Science | Vikid’s Bash Cookbook |
Bash case statements
Here is the truth about case in Bash: it is not a weaker if – it is a pattern matcher, and patterns are where it earns its keep. When you find yourself writing a chain of [[ $x == … ]] branches, case is usually clearer.
What it is, in plain words
case matches a string against a list of glob patterns and runs the commands for the first match:
case "$word" in
pattern1) commands ;;
pattern2|pattern3) commands ;;
*) commands ;; # default
esac
Patterns are the same style as filename globs: * any string, ? any one character, [abc] a set, and with shopt -s extglob, richer forms like +(foo|bar).
Why it matters
Option parsing, file-type dispatch, answering “what kind of argument is this?” – case expresses these without nested ifs. It is also POSIX, so scripts using it run under dash and other /bin/sh implementations. A surprising amount of system init and package-manager scripting is case all the way down.
An everyday analogy: a sorting desk
Letters arrive. A clerk glances at the postcode pattern and drops each into a bin: “starts with SW”, “ends with 4AE”, “everything else”. The clerk does not open the letters (if with complicated tests); they match the address against stencils. That’s case.
A hands-on example
#!/bin/bash
# greet.sh
name=${1:-world}
case "$name" in
[Vv]ikid|VikiD) echo "Hello, me." ;;
vikix|void) echo "Hello, machine." ;;
*.local) echo "Hello, LAN host $name" ;;
"") echo "Hello, nobody?" ;;
*) echo "Hello, $name." ;;
esac
File-extension dispatch:
case "$file" in
*.tar.gz|*.tgz) tar -tzf "$file" ;;
*.tar.xz) tar -tJf "$file" ;;
*.zip) unzip -l "$file" ;;
*.md) "${EDITOR:-nvim}" "$file" ;;
*) echo "unknown type: $file" >&2 ; exit 1 ;;
esac
Simple option parsing (for fuller needs, use getopts):
while [ $# -gt 0 ]; do
case "$1" in
-v|--verbose) verbose=1 ; shift ;;
-o|--output) output=$2 ; shift 2 ;;
--) shift ; break ;;
-*) echo "unknown: $1" >&2 ; exit 1 ;;
*) break ;;
esac
done
;;& (Bash) falls through to test the next pattern; ;& falls through and runs the next arm. Prefer explicit patterns joined with | unless you need fall-through.
Patterns in detail
# character classes and ranges
case "$c" in
[a-z]) echo lower ;;
[A-Z]) echo upper ;;
[0-9]) echo digit ;;
esac
# extglob (Bash)
shopt -s extglob
case "$file" in
+(*.jpg|*.png)) echo image ;;
!(*.tmp)) echo "not a temp file" ;;
esac
Nested case is fine; so is calling a function from each arm to keep the structure readable. When the patterns are really regular expressions, step up to [[ $s =~ ^[0-9]+$ ]] or pipe through grep -E – don’t force case to be something it isn’t.
case versus if
Use if when the test is a condition (-f file, -z "$s", numeric comparison). Use case when the test is “which of these shapes does this string match?”. Mixing them – case for the shape, if inside an arm for a deeper check – is normal and clear.
Common misconceptions
- "
caseuses regex." It uses globs.*.txtworks;.*\.txtdoes not mean what you think. - “Quotes break patterns.” Quote the subject (
"$word"), not the patterns. - "
*)is required." No, but a default that errors is good manners in scripts. - "
caseis slower thanif." Negligible. Choose the clearer form. - “Only Bash has it.”
caseis POSIX; it’s one of the portable conditionals.
Connections
- BASH, Bash if, Bash Variables.
- Bash Reserve Words, Bash Cookbook.
- Regular Expressions – when you really do need regex (
[[ =~ ]]).
#!/bin/bash
CHAR=$1
case $CHAR in
[a-z])
echo "Small Alphabet." ;;
[A-Z])
echo "Big Alphabet." ;;
[0-9])
echo "Number." ;;
*)
echo "Special Character."
esac
