Bash case Statement: Match Patterns in Shell Scripts

By 

Updated on

6 min read

Bash case statement syntax and pattern matching examples

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

txt
case EXPRESSION in

  PATTERN_1)
    STATEMENTS
    ;;

  PATTERN_2)
    STATEMENTS
    ;;

  *)
    STATEMENTS
    ;;

esac
  • The statement opens with the case keyword, followed by the expression to match and the in keyword, and closes with esac.
  • 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 case statement exits with status 0.
  • 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:

languages.shsh
#!/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"
    ;;
esac

Run the script:

Terminal
bash languages.sh

If you enter “Lithuania”, the first pattern matches and the script prints:

output
Enter the name of a country: Lithuania
The official language of Lithuania is Lithuanian

If you enter a country that does not match any named pattern, the default * clause runs:

output
Enter the name of a country: Argentina
The official language of Argentina is unknown

Multiple Patterns

Use the | operator to match several values in one clause. Here is a script that handles a yes/no prompt:

confirm.shsh
#!/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
    ;;
esac

Argument 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:

service.shsh
#!/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
    ;;
esac

Call the script with an argument:

Terminal
bash service.sh start
output
Starting the service...

If you pass an unrecognised argument, the default clause prints usage instructions:

output
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:

PatternMatches
*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:

classify.shsh
#!/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"
    ;;
esac
Terminal
bash classify.sh report.md
output
report.md is a text file

Case-Insensitive Matching

By default, case pattern matching is case-sensitive. To match patterns regardless of case, enable the nocasematch shell option before the statement:

Terminal
shopt -s nocasematch

case $ANSWER in
  yes) echo "You said yes" ;;
  no)  echo "You said no"  ;;
esac

shopt -u nocasematch

Disable 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:

flags.shsh
#!/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"
    ;;

esac
output
Has admin access
Can edit content
Can view content

Quick Reference

For a printable quick reference, see the Bash cheatsheet .

SyntaxDescription
case VAR inOpen 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
esacClose the case statement
shopt -s nocasematchEnable 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

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