Bash case Statement: Match Patterns in Shell Scripts

The Bash case statement simplifies complex conditionals when a single value needs to be compared against multiple patterns. It is cleaner and easier to read than a chain of nested if/elif
statements, especially when there are more than two or three possible values.
The case statement works similarly to the switch statement in JavaScript or C. The main difference is that Bash case does not fall through to the next clause after a match by default — it stops and exits the statement once the first matching pattern executes.
This guide explains how to write Bash case statements, use glob patterns, handle command-line arguments, and control fall-through behavior.
case Statement Syntax
case EXPRESSION in
PATTERN_1)
STATEMENTS
;;
PATTERN_2)
STATEMENTS
;;
*)
STATEMENTS
;;
esac- The statement opens with the
casekeyword, followed by the expression to match and theinkeyword, and closes withesac. - Each pattern is followed by
)and ends with;;to terminate the clause. - You can list multiple patterns in one clause by separating them with
|. - The wildcard
*as the last pattern acts as a default — it matches anything not caught above. - If no pattern matches and there is no default, the
casestatement exits with status0. - Otherwise, the exit status is that of the last command executed in the matching clause.
Basic Example
Here is a script that prints the official language of a given country:
#!/bin/bash
echo -n "Enter the name of a country: "
read COUNTRY
echo -n "The official language of $COUNTRY is "
case $COUNTRY in
Lithuania)
echo "Lithuanian"
;;
Romania | Moldova)
echo "Romanian"
;;
Italy | "San Marino" | Switzerland | "Vatican City")
echo "Italian"
;;
*)
echo "unknown"
;;
esacRun the script:
bash languages.shIf you enter “Lithuania”, the first pattern matches and the script prints:
Enter the name of a country: Lithuania
The official language of Lithuania is LithuanianIf you enter a country that does not match any named pattern, the default * clause runs:
Enter the name of a country: Argentina
The official language of Argentina is unknownMultiple Patterns
Use the | operator to match several values in one clause. Here is a script that handles a yes/no prompt:
#!/bin/bash
echo -n "Do you want to continue? [y/n]: "
read ANSWER
case $ANSWER in
y | Y | yes | Yes | YES)
echo "Continuing..."
;;
n | N | no | No | NO)
echo "Aborted."
exit 1
;;
*)
echo "Invalid input. Please enter y or n."
exit 1
;;
esacArgument Parsing
One of the most common uses of case in shell scripting is parsing command-line arguments. The following example mimics a basic service control script:
#!/bin/bash
case $1 in
start)
echo "Starting the service..."
;;
stop)
echo "Stopping the service..."
;;
restart)
echo "Restarting the service..."
;;
status)
echo "Checking service status..."
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esacCall the script with an argument:
bash service.sh startStarting the service...If you pass an unrecognised argument, the default clause prints usage instructions:
Usage: ./service.sh {start|stop|restart|status}For more advanced argument parsing, see the getopts
guide.
Glob Pattern Matching
case patterns support the same glob characters available in filename matching:
| Pattern | Matches |
|---|---|
* | Any string, including empty |
? | Any single character |
[abc] | Any one character listed in the brackets |
[a-z] | Any one character in the range |
Here is an example that classifies a filename by extension:
#!/bin/bash
FILE=$1
case $FILE in
*.sh)
echo "$FILE is a shell script"
;;
*.py)
echo "$FILE is a Python script"
;;
*.jpg | *.jpeg | *.png | *.gif)
echo "$FILE is an image"
;;
*.txt | *.md)
echo "$FILE is a text file"
;;
*)
echo "$FILE: unknown file type"
;;
esacbash classify.sh report.mdreport.md is a text fileCase-Insensitive Matching
By default, case pattern matching is case-sensitive. To match patterns regardless of case, enable the nocasematch shell option before the statement:
shopt -s nocasematch
case $ANSWER in
yes) echo "You said yes" ;;
no) echo "You said no" ;;
esac
shopt -u nocasematchDisable nocasematch with shopt -u after the block so it does not affect the rest of the script.
Fall-Through Terminators
By default, Bash stops after the first matching clause. Two alternative terminators change this behavior:
;&— execute the next clause’s commands unconditionally, without testing its pattern.;;&— continue testing the remaining patterns for additional matches.
Here is an example using ;;& to match a value against multiple independent patterns:
#!/bin/bash
VALUE="admin"
case $VALUE in
admin)
echo "Has admin access"
;;&
admin | editor)
echo "Can edit content"
;;&
admin | editor | viewer)
echo "Can view content"
;;
esacHas admin access
Can edit content
Can view contentQuick Reference
For a printable quick reference, see the Bash cheatsheet .
| Syntax | Description |
|---|---|
case VAR in | Open a case statement on VAR |
PATTERN) | Start a clause matching PATTERN |
P1 | P2) | Match either P1 or P2 |
*) | Default clause — matches everything |
;; | End clause, stop matching |
;& | End clause, run next clause unconditionally |
;;& | End clause, continue testing patterns |
esac | Close the case statement |
shopt -s nocasematch | Enable case-insensitive matching |
Troubleshooting
Pattern does not match as expected
Remember that case uses glob patterns, not regex. Use *, ?, and bracket ranges ([a-z]) or switch to [[ =~ ]] for regex checks.
Input with spaces does not match
Quote patterns that contain spaces (for example, "San Marino"). Also quote variables in surrounding commands when needed to avoid word splitting.
Case-insensitive match affects other parts of the script
If you enable shopt -s nocasematch, disable it with shopt -u nocasematch after the case block to avoid side effects.
Unexpected fall-through behavior
Use ;; for standard behavior. ;& always runs the next clause, while ;;& retests the remaining patterns, which can trigger multiple clauses.
Script exits with the wrong status
The case statement returns the status of the last command in the executed clause. Set explicit exit codes in error/default branches when writing scripts.
FAQ
When should I use case instead of if/elif?
Use case when you are comparing one variable or expression against three or more fixed values or patterns. For two conditions or complex boolean logic, if/elif
is clearer. For interactive menus with numbered choices, consider select
.
Can I match numbers with case?
Yes. case matches strings, but numbers written as strings work fine. For arithmetic comparisons (greater than, less than), use an if statement with comparison operators
instead.
What happens if no pattern matches and there is no * default?
The case statement exits silently with status 0. No error is produced and no clause runs. Adding a *) default clause is good practice to handle unexpected input explicitly.
Can I use regular expressions in case patterns?
No. case uses glob patterns, not regular expressions. For regex matching, use the [[ =~ ]] operator inside an if statement.
Conclusion
The Bash case statement is a clean and readable way to branch on a single value across multiple patterns. It handles simple string matching, glob patterns, and command-line arguments well. For more on controlling script flow, see the guides on if/elif
, for loops
, and positional parameters
.
Tags
Linuxize Weekly Newsletter
A quick weekly roundup of new tutorials, news, and tips.
About the authors

Dejan Panovski
Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 800+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.
View author page