VMware ESXi and vSphere Cluster Management

Bash case Statement: Multiway Selection in Linux Shell Scripts

Learn Bash case syntax, patterns, default branches, interactive menus, terminators, testing, and troubleshooting with practical Linux shell examples.

What Is a Bash case Statement?

A case statement is a Bash control structure that chooses one command block by matching a single value against multiple patterns. This is called multiway branching because there are more than two possible execution paths.

Use case when one input can have several alternatives, such as menu choices, command options, or response values. It is often clearer than a long chain of if/elif conditions that all test the same variable.

Other programming languages commonly provide a similar construct called a switch statement. Bash calls its version case.

Basic Bash case Syntax

case EXPRESSION in
    PATTERN_1)
        commands
        ;;
    PATTERN_2)
        commands
        ;;
    *)
        default commands
        ;;
esac

The expression is the value Bash evaluates. A branch consists of a pattern followed by a closing parenthesis and the commands that should run when that pattern matches. The normal terminator is ;;. The * pattern is usually the default branch, and esac closes the construct. esac is case spelled backward.

Indent each pattern and its commands consistently. Indentation is not required by Bash, but it makes branches easier to read and maintain.

Parts of a Bash case Statement

  • Syntax element: case | Role: Starts the selection structure | Example use: case "$CHOICE" in
  • Syntax element: Expression | Role: Value being evaluated | Example use: "$CHOICE"
  • Syntax element: in | Role: Separates the expression from the branch patterns | Example use: case "$CHOICE" in
  • Syntax element: Pattern | Role: Possible match for the expression | Example use: 1)
  • Syntax element: ;; | Role: Ends the selected branch and stops processing | Example use: pwd ;;
  • Syntax element: * | Role: Fallback for values without another match | Example use: *)
  • Syntax element: esac | Role: Closes the statement | Example use: esac

How Matching and Execution Work

  1. Bash evaluates the case expression.
  2. It compares that value with the branch patterns from top to bottom.
  3. When the first matching pattern is found, Bash runs that branch's commands.
  4. The normal ;; terminator ends case processing, so later branches are not checked.

If no explicit pattern matches and there is no * branch, Bash does nothing for the case statement and continues with the command after esac. A default branch is therefore useful when unexpected input must produce a message or another safe action.

Interactive Numbered Menu Example

The following script reads a menu selection and uses case to run one of three Linux commands.

#!/usr/bin/env bash

printf '%s\n' 'Choose an action:'
printf '%s\n' '1) Show the working directory'
printf '%s\n' '2) Show the current date and time'
printf '%s\n' '3) List files in detail'
read -r -p 'Selection: ' CHOICE

case "$CHOICE" in
    1)
        pwd
        ;;
    2)
        date
        ;;
    3)
        ls -l
        ;;
    *)
        printf 'Invalid selection: %s\n' "$CHOICE"
        ;;
esac

read -r stores the user's input in the descriptive variable CHOICE. Quoting "$CHOICE" makes the expression safe when the value is empty or contains spaces. The patterns 1), 2), and 3) match the valid menu choices. The * branch handles unsupported numbers, text, and empty input.

Menu Input and Result

  • User input: 1 | Matching branch: 1) | Command or message produced: pwd prints the current working directory
  • User input: 2 | Matching branch: 2) | Command or message produced: date prints the current date and time
  • User input: 3 | Matching branch: 3) | Command or message produced: ls -l prints a detailed directory listing
  • User input: Unsupported number | Matching branch: *) | Command or message produced: An invalid-selection message
  • User input: Non-numeric text or empty input | Matching branch: *) | Command or message produced: An invalid-selection message

Patterns and Pattern Alternatives

A case label is a pattern. Patterns can match literal values or use shell glob syntax.

Multiple Alternatives with |

Place several alternatives in one branch with the pipe character. This is useful when several spellings should produce the same result.

read -r -p 'Continue? [y/n] ' ANSWER

case "$ANSWER" in
    y|Y|yes|YES)
        printf '%s\n' 'Continuing.'
        ;;
    n|N|no|NO)
        printf '%s\n' 'Stopping.'
        ;;
    *)
        printf '%s\n' 'Please answer yes or no.'
        ;;
esac

Common Glob Patterns

  • Pattern form: Literal text | What it matches: That exact value | Example: start)
  • Pattern form: * | What it matches: Any sequence of characters, including an empty sequence | Example: *.log
  • Pattern form: ? | What it matches: Exactly one character | Example: ?
  • Pattern form: [0-9] | What it matches: One character in the range zero through nine | Example: [0-9])
  • Pattern form: [Yy] | What it matches: Either uppercase or lowercase Y | Example: [Yy]|[Yy][Ee][Ss])

Character ranges can provide cautious case-insensitive matching, for example [Yy][Ee][Ss] for “yes” in either capitalization. Bash extended patterns can also help with more advanced matching when enabled with shopt -s extglob, but explicit alternatives and character ranges are easier for beginners to audit.

Filename Category Selector

Wildcard patterns are useful for categorizing filenames. Put specific patterns before the catch-all pattern.

read -r -p 'Filename: ' FILE

case "$FILE" in
    *.txt)
        printf '%s\n' 'This is a text file.'
        ;;
    *.log)
        printf '%s\n' 'This is a log file.'
        ;;
    *.sh)
        printf '%s\n' 'This is a shell script.'
        ;;
    *)
        printf '%s\n' 'Unknown or unsupported file type.'
        ;;
esac

The pattern *.txt matches a filename ending in .txt. The fallback * should remain last. If it appears first, it can match every value and prevent the specific branches from being reached.

Quoting Expressions and Using Variables

A case expression is commonly a variable expansion such as $CHOICE. Use descriptive names such as CHOICE, ANSWER, or FILE instead of vague names such as x.

case "$CHOICE" in
    start)
        printf '%s\n' 'Starting.'
        ;;
    stop)
        printf '%s\n' 'Stopping.'
        ;;
esac

Quoting the expansion as "$CHOICE" clearly preserves the value when it contains spaces and handles an empty value safely. Bash case matching still compares the resulting value against the patterns.

Branch Terminators

The standard ;; terminator means “finish this branch and stop processing the case statement.” It is the correct choice for ordinary menus and one-result decisions.

Bash also supports advanced continuation terminators:

  • ;& runs the next branch's commands after a match without testing the next pattern.
  • ;&& continues testing subsequent patterns and runs the commands of each later matching branch.

These are Bash-specific behaviors and can cause commands from an unexpected branch to run if used accidentally. Prefer ;; unless continuation is intentional.

case Versus if/elif

  • Situation: Many alternatives for one input value | Preferred construct: case | Reason: Branches are grouped around one expression and are easy to scan.
  • Situation: Command options or menu selections | Preferred construct: case | Reason: Literal and pattern-based choices map naturally to branches.
  • Situation: General boolean tests | Preferred construct: if | Reason: if directly expresses compound conditions.
  • Situation: File tests or numeric comparisons | Preferred construct: if | Reason: Tests such as -f, -d, -eq, and -gt fit conditional expressions.
  • Situation: Unrelated conditions | Preferred construct: if | Reason: The conditions do not represent alternatives for one value.

Save, Check, and Run a Script

Save the menu example in a file named menu.sh. The shebang #!/usr/bin/env bash asks the environment to locate Bash.

bash -n menu.sh
chmod +x menu.sh
./menu.sh
bash menu.sh

bash -n menu.sh checks syntax without running the script. chmod +x menu.sh adds execute permission, allowing ./menu.sh. Running bash menu.sh invokes Bash explicitly and does not require the executable permission bit.

If installed, shellcheck menu.sh can identify common shell scripting issues in addition to syntax checking.

Testing Checklist

  • Enter 1 and confirm that the working directory is printed.
  • Enter 2 and confirm that the date and time are printed.
  • Enter 3 and confirm that a detailed listing appears.
  • Enter an unsupported number and confirm that the default message appears.
  • Enter non-numeric text and press Enter on an empty input to test fallback handling.

Troubleshooting

Every Input Reaches the Invalid Branch

Check that the patterns match the actual input, that the same variable is used by read and case, and that unexpected whitespace or characters are not present. During debugging, print the captured value with a command such as printf '<%s>\n' "$CHOICE". Make sure intended patterns appear before *.

Syntax Error Near esac

A branch may be missing its closing parenthesis, its ;;, the in keyword, or the closing esac. Compare the script with the canonical layout and run bash -n menu.sh.

A Specific Branch Never Runs

The catch-all * branch or another broad wildcard may appear before the intended branch. Move specific patterns earlier and place * last. Also check capitalization and add accepted alternatives such as y|Y when needed.

Unexpected Commands Also Execute

Look for accidental use of ;& or ;&&. Replace the continuation terminator with ;; for normal one-branch behavior.

The Script Fails When Run Directly

Run chmod +x menu.sh and verify that the first line contains a valid Bash shebang. Alternatively, run the file explicitly with bash menu.sh.

Summary

  • case selects among multiple branches by matching one expression against shell patterns.
  • Each branch has a pattern, commands, and normally ends with ;;.
  • The * branch provides a fallback and usually belongs last.
  • Use |, glob patterns, wildcards, and bracket expressions to describe accepted values.
  • Quote variable expressions such as "$CHOICE", validate input, and check syntax with bash -n.
  • Prefer case for menus and many alternatives for one value; prefer if for general tests and comparisons.

Continue with the Bash case statement lesson when you need a quick reference for this construct.