| |

Bash Scripting 07 ๐Ÿง Case Statements โ€” Pattern matching and multi-branch logic

โฑ๏ธ Estimated read: 11 min | |

A script that branches on a single variable against many possible values is the natural home of the case statement. The previous chapter covered if/elif/else chains, which work for any condition but become difficult to read when the same variable is compared against five, ten, or twenty possibilities. The case construct is designed for exactly this scenario: one word is tested against a list of patterns, and the first matching pattern’s command list is executed . It is not a replacement for if; it is a specialized tool for a specific shape of decision.

The syntax is compact and the semantics are glob-based rather than regular-expression-based. The word undergoes expansion before matching, each pattern is expanded, and the comparison uses shell pattern matchingโ€”*, ?, and bracket expressionsโ€”not regex . This distinction matters: case is simpler than regex and more powerful than literal string comparison. It is the construct behind menu-driven scripts, argument parsers, and the service management scripts that every Linux administrator has read.

Key point: The case statement matches one word against a list of glob patterns and executes the first matching clause. Every clause must end with ;;, and the final pattern is conventionally *) to catch anything that did not match earlier patterns . The word and patterns undergo expansion before matching, but the patterns are not regular expressionsโ€”they are the same patterns used in filename globbing.


Why case statements exist

The readability problem.

An if/elif chain that compares the same variable to ten different strings is technically correct and practically unreadable. The variable name is repeated in every condition, the comparison operator is repeated, and the branch bodies are buried between lines of test syntax. The case statement moves the variable to the top and lists the patterns in a compact block, making the set of accepted values visible at a glance . This is the primary reason case exists: it is the readable form of a multi-way string comparison.

The pattern-matching problem.

Literal string comparison is often insufficient. A script that accepts yes, YES, and y as equivalent must list all three in an if chain or use a case-insensitive comparison. A script that processes files by extension must check for .txt, .jpg, .png, and an unknown fallback. The case statement’s patterns support globbing: [yY]* matches any string starting with y or Y, and *.jpg|*.png matches either extension . This pattern capability is built into the construct, not added through external tools.

The fall-through problem.

Sometimes a clause should not terminate the case statement. It should continue into the next clause, either unconditionally or by testing the next pattern. Bash provides two operators beyond ;; for these cases: ;& executes the next clause’s commands without testing its pattern, and ;;& tests the next clause’s pattern and executes its commands if it matches . These operators are rare in simple scripts, but they allow case to express logic that would otherwise require duplicated command lists or nested conditionals.


a. The case syntax and pattern matching

The case statement begins with case word in, followed by one or more clauses, and ends with esacโ€”case spelled backwards . Each clause consists of one or more patterns separated by |, a closing parenthesis ), a command list, and a terminator.

#!/usr/bin/env bash
# Match a single word against multiple patterns
read -r answer
case "$answer" in
    yes | y | Y | YES)
        echo "Confirmed"        # Matches any of the four patterns
        ;;
    no | n | N | NO)
        echo "Declined"         # Matches any of the four patterns
        ;;
    *)
        echo "Unrecognized"     # Catch-all for anything else
        ;;
esac

The | operator separates patterns that share the same command list . The *) pattern is the default case because it matches any string, and it is conventionally placed last so that more specific patterns are tested first . If no pattern matches and there is no *), the case statement returns a zero exit status without executing any command list .

The patterns are glob patterns, not regular expressions. * matches any sequence of characters, ? matches a single character, and [abc] matches any one of the characters inside the brackets . A pattern like [0-9]* matches any string that begins with a digit. A pattern like *.log matches any string ending in .log. These are the same patterns the shell uses for filename expansion, and they are evaluated with the same rules.


b. Terminators and fall-through behavior

The terminator after a command list determines what happens after the clause executes. The default ;; stops the entire case statement . The two alternatives, ;& and ;;&, change the control flow.

#!/usr/bin/env bash
# Demonstrate ;;& (test next pattern)
case "$1" in
    start*)
        echo "Starting..."      # Runs if $1 begins with "start"
        ;;&                     # Continue testing the next pattern
    *debug*)
        echo "Debug mode"       # Also runs if $1 contains "debug"
        ;;
    *)
        echo "Unknown command"
        ;;
esac

With ;;&, the shell tests the next clause’s pattern even after a match. If the next pattern also matches, its command list runs as well . This is useful when the patterns are not mutually exclusiveโ€”a command like start-debug matches both start* and *debug*, and the script may want to execute both clauses. The ;& operator is different: it executes the next clause’s commands unconditionally, without testing its pattern, which is useful when a clause is a prefix that should always be followed by a shared cleanup or continuation block.

These operators are Bash extensions, not POSIX. A script intended for /bin/sh must use ;; exclusively and restructure the logic if fall-through is needed.


c. Case versus if: choosing the right construct

The choice between case and if is determined by the shape of the comparison, not by preference. case is for one word against many patterns. if is for any condition, including numeric comparisons, file tests, and combinations of multiple variables.

#!/usr/bin/env bash
# Case is correct: one variable, many constant patterns
case "$1" in
    start)   systemctl start "$2" ;;
    stop)    systemctl stop "$2" ;;
    restart) systemctl restart "$2" ;;
    status)  systemctl status "$2" ;;
    *)       echo "Usage: $0 {start|stop|restart|status} <service>" >&2; exit 1 ;;
esac

# If is correct: numeric comparison and file test
if [ "$count" -gt 10 ] && [ -f "$logfile" ]; then
    echo "Large count and log exists"
fi

The case version is compact and readable because every branch tests the same variable against a literal pattern. The if version is necessary because the condition combines a numeric comparison with a file test, which case cannot express . When the comparisons are all against the same word and the patterns are constants or globs, case is the clearer construct. When the conditions involve multiple variables, numeric operators, or file tests, if is the only option.


Complete Example Session

#!/usr/bin/env bash
# ============================================
# PART 1: THE SERVICE MANAGEMENT MENU
# ============================================
# A menu-driven script that uses case to dispatch commands
service="$2"

if [ -z "$service" ]; then
    echo "Usage: $0 {start|stop|restart|status|enable|disable} <service>" >&2
    exit 1
fi

# ============================================
# PART 2: THE CASE DISPATCH
# ============================================
case "$1" in
    start)
        echo "Starting $service"
        systemctl start "$service"
        ;;
    stop)
        echo "Stopping $service"
        systemctl stop "$service"
        ;;
    restart)
        echo "Restarting $service"
        systemctl restart "$service"
        ;;

# ============================================
# PART 3: GROUPED PATTERNS
# ============================================
    # Both "status" and "stat" trigger the same command
    status | stat)
        systemctl status "$service" --no-pager
        ;;

# ============================================
# PART 4: PATTERN MATCHING
# ============================================
    # Any argument starting with "en" enables the service
    en*)
        echo "Enabling $service"
        systemctl enable "$service"
        ;;

# ============================================
# PART 5: THE DEFAULT CASE
# ============================================
    *)
        echo "Unknown command: $1" >&2
        echo "Valid commands: start, stop, restart, status, enable" >&2
        exit 1
        ;;
esac

# ============================================
# PART 6: FALL-THROUGH WITH ;;&
# ============================================
case "$1" in
    start*)
        echo "Action begins with start"
        ;;&                     # Continue testing subsequent patterns
    *debug*)
        echo "Debug flag detected"
        ;;
    *)
        :                       # No-op; the default is irrelevant here
        ;;
esac

The six parts demonstrate the core patterns: a menu dispatcher with grouped patterns, a glob pattern (en*) that matches multiple literal strings, a *) default that catches invalid input, and a ;;& fall-through that tests multiple patterns against the same word. The first case is the typical argument parser; the second demonstrates the non-default terminator.


Quick Reference

Case Syntax

ElementSyntaxPurpose
Opencase "$var" inBegin the statement
Patternpattern)Match this glob pattern
ORp1 | p2)Match either pattern
Terminator;;End clause; exit case
Fall-through;&Execute next clause unconditionally
Continue testing;;&Test next clause’s pattern
Default*)Catch-all (place last)
CloseesacEnd the statement

Pattern Types

PatternMatchesExample
*Any string*) = default
?Any single characterfile?.txt
[abc]Any one listed character[yYnN]
[0-9]Any character in range[0-9]*
p1|p2Either patternyes|no

Case vs If

Use CaseConstruct
One variable, many constantscase
Glob patterns on one variablecase
Numeric comparisonif with -eq, -gt
File testsif with -f, -d
Combined conditionsif with &&, ||

Best Practices

โœ… Do This:

# Quote the word being tested
case "$1" in ... esac                                       # โœ…

# Place *) last
*) echo "Unknown"; exit 1 ;;                                # โœ…

# Use | for patterns sharing a command
yes | y | Y) echo "Confirmed" ;;                            # โœ…

# Use ;; as the default terminator
start) echo "Starting" ;;                                   # โœ…

# Use ;;& when multiple patterns may match
start*) echo "Start"; ;;&                                   # โœ…

# Exit with non-zero status in the default branch
*) echo "Usage: $0 ..." >&2; exit 1 ;;                      # โœ…

โŒ Don’t Do This:

# Omit the ;; terminator
start) echo "Starting"    # Syntax error or fall-through     # โŒ

# Place *) before specific patterns
*) echo "Unknown" ;; start) echo "Start" ;;    # * always wins # โŒ

# Use regex syntax in patterns
[0-9]+) echo "Number" ;;    # + is literal, not regex        # โŒ

# Use case for numeric comparison
case "$count" in 10) ... ;; esac    # Only matches literal 10 # โŒ

# Forget esac
case "$1" in start) echo "Start" ;;    # Syntax error        # โŒ

Common Pitfalls

PitfallWhy It HappensFix
*) always matchesPlaced before specific patternsMove *) to the last clause
Pattern does not match as expectedUsed regex syntax instead of globUse *, ?, []
Syntax error: unexpected tokenMissing ;; or esacCheck each clause terminates
Unquoted word splits$var contains spacesQuote: case "$var" in
;& and ;;& not availableScript uses /bin/shUse ;; or switch to Bash

Real-World Examples

1. Basic menu dispatch

case "$1" in
    start)   echo "Starting" ;;
    stop)    echo "Stopping" ;;
    restart) echo "Restarting" ;;
    *)       echo "Unknown" ;;
esac

2. Grouped patterns

case "$answer" in
    yes | y | Y) echo "Confirmed" ;;
    no | n | N)  echo "Declined" ;;
esac

3. File extension matching

case "$file" in
    *.txt)         echo "Text file" ;;
    *.jpg | *.png) echo "Image" ;;
    *)             echo "Unknown" ;;
esac

4. Numeric range with glob

case "$percent" in
    [1-6]*) echo "Low" ;;
    [7-8]*) echo "Medium" ;;
    9[0-9]) echo "High" ;;
esac

5. Argument with empty check

case "${1:-}" in
    "") echo "Usage: $0 <arg>" >&2; exit 1 ;;
esac

6. Fall-through with ;&

case "$1" in
    verbose) set -x ;&
    start)   echo "Starting" ;;
esac

7. Continue testing with ;;&

case "$1" in
    start*) echo "Start" ;;&
    *debug*) echo "Debug" ;;
esac

8. Case-insensitive with nocasematch

shopt -s nocasematch
case "$answer" in
    yes) echo "Confirmed" ;;
esac

9. Default with exit status

case "$1" in
    start) echo "Start" ;;
    *)     echo "Invalid" >&2; exit 1 ;;
esac

10. Initscript pattern

case "$1" in
    start)   start ;;
    stop)    stop ;;
    restart) stop; start ;;
    status)  status ;;
    *)       echo "Usage: $0 {start|stop|restart|status}" ;;
esac

Visual

Case Statement Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  CASE TESTS ONE WORD AGAINST PATTERNS IN ORDER               โ”‚
โ”‚                                                              โ”‚
โ”‚  case "$1" in                                                โ”‚
โ”‚                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ Pattern 1: start)                                     โ”‚  โ”‚
โ”‚  โ”‚   โ†“ matches?                                           โ”‚  โ”‚
โ”‚  โ”‚   YES โ†’ run commands โ†’ ;; โ†’ EXIT CASE                 โ”‚  โ”‚
โ”‚  โ”‚   NO  โ†’ continue                                       โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ Pattern 2: stop)                                      โ”‚  โ”‚
โ”‚  โ”‚   โ†“ matches?                                           โ”‚  โ”‚
โ”‚  โ”‚   YES โ†’ run commands โ†’ ;; โ†’ EXIT CASE                 โ”‚  โ”‚
โ”‚  โ”‚   NO  โ†’ continue                                       โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ Pattern 3: *)  (default)                              โ”‚  โ”‚
โ”‚  โ”‚   โ†“ always matches                                     โ”‚  โ”‚
โ”‚  โ”‚   YES โ†’ run commands                                   โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                              โ”‚
โ”‚  esac                                                        โ”‚
โ”‚                                                              โ”‚
โ”‚  First match wins. *) is the catch-all.                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Terminator Behavior

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  THREE TERMINATORS, THREE BEHAVIORS                          โ”‚
โ”‚                                                              โ”‚
โ”‚  ;;  (default)                                               โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ Match โ†’ run commands โ†’ EXIT case                       โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                              โ”‚
โ”‚  ;&  (fall through)                                          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ Match โ†’ run commands โ†’ run NEXT clause's commands      โ”‚  โ”‚
โ”‚  โ”‚         (without testing its pattern)                  โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                              โ”‚
โ”‚  ;;& (continue testing)                                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ Match โ†’ run commands โ†’ TEST next clause's pattern      โ”‚  โ”‚
โ”‚  โ”‚         โ†’ if match, run its commands                   โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                              โ”‚
โ”‚  ;;& is for overlapping patterns.                            โ”‚
โ”‚  ;& is for shared cleanup/continuation.                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Case vs If Decision

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  WHICH CONSTRUCT?                                            โ”‚
โ”‚                                                              โ”‚
โ”‚  Is the comparison about ONE variable?                       โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ”œโ”€โ”€ YES โ†’ Are the patterns constants or globs?         โ”‚
โ”‚       โ”‚         โ”‚                                            โ”‚
โ”‚       โ”‚         โ”œโ”€โ”€ YES โ†’ Use CASE                           โ”‚
โ”‚       โ”‚         โ”‚         (readable, compact)                โ”‚
โ”‚       โ”‚         โ”‚                                            โ”‚
โ”‚       โ”‚         โ””โ”€โ”€ NO  โ†’ Use IF with [[ ]]                  โ”‚
โ”‚       โ”‚                   (regex, numeric)                   โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ””โ”€โ”€ NO  โ†’ Use IF                                       โ”‚
โ”‚                 (multiple variables, file tests, math)       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Syntaxcase "$var" in pattern) ... ;; esac
Pattern typeGlob (*, ?, [abc]), not regex
OR operatorp1 | p2 matches either pattern
Default*) catches unmatched values (place last)
Default terminator;; exits the case statement
Fall-through;& runs next clause unconditionally
Continue testing;;& tests next pattern
Return status0 if no match; last command’s status otherwise
Best forOne variable, many constants, menus, parsers
Not forNumeric comparisons, file tests, combined conditions

Key takeaways:

  • case matches one word against glob patterns. It is the readable form of a multi-way string comparison. The patterns are the same as filename globbing, not regular expressions .
  • Every clause ends with ;; unless fall-through is intended. The ;; terminator stops the case statement after the first match. ;& and ;;& are Bash extensions for fall-through and continue-testing .
  • *) is the default and belongs last. It matches any string. Placing it before specific patterns makes those patterns unreachable.
  • Use | for patterns sharing a command. yes | y | Y) is cleaner than three separate clauses with identical bodies .
  • case is not for numeric comparisons or file tests. Those require if with -eq, -gt, -f, and -d. case compares strings against patterns only .
  • Quote the word being tested. case "$1" in prevents word splitting when the argument contains spaces. An unquoted empty variable can cause a syntax error.
  • nocasematch enables case-insensitive matching. shopt -s nocasematch makes yes match YES without listing both patterns.

Remember: The case statement is a dispatcher. It takes one word and routes it to the first matching pattern’s command list. The patterns are globs, the terminator is ;; by default, and *) is the catch-all that must be last. Use it when the decision is one variable against many possibilities. Use if when the decision involves numbers, files, or multiple variables. The two constructs are complementary, not competing. A well-written script uses both where each is clearest.


Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!