Shell by Example: Case Statements POSIX + Bash

A case statement matches a value against patterns. It’s the shell’s equivalent to a switch statement.

Syntax breakdown:

  • case VALUE in starts the statement
  • pattern) defines a pattern to match (note the ))
  • ;; ends each pattern block (like break)
  • esac ends the statement (case spelled backward)
Edit
#!/bin/sh
fruit="apple"

case $fruit in
    apple)
        echo "It's an apple"
        ;;
esac
Output:
It's an apple

A case statement can have multiple pattern blocks. The shell tests each pattern in order and executes the first matching block.

Edit
#!/bin/sh
color="blue"

case $color in
    red)
        echo "Color is red"
        ;;
    green)
        echo "Color is green"
        ;;
    blue)
        echo "Color is blue"
        ;;
esac
Output:
Color is blue

The * pattern matches anything. Place it last to handle values that don’t match other patterns. This acts like a default or “else” branch.

Edit
#!/bin/sh
animal="cat"

case $animal in
    dog)
        echo "It's a dog"
        ;;
    bird)
        echo "It's a bird"
        ;;
    *)
        echo "Unknown animal: $animal"
        ;;
esac
Output:
Unknown animal: cat

Use | to match multiple patterns with the same action. This is useful when several values should behave the same.

Edit
#!/bin/sh
day="saturday"

case $day in
    monday | tuesday | wednesday | thursday | friday)
        echo "It's a weekday"
        ;;
    saturday | sunday)
        echo "It's a weekend"
        ;;
esac
Output:
It's a weekend

Case patterns support glob-style wildcards. Use * to match any string of characters. This is powerful for matching prefixes or suffixes.

Edit
#!/bin/sh
file="script.sh"

case $file in
    *.sh)
        echo "$file is a shell script"
        ;;
    *.txt)
        echo "$file is a text file"
        ;;
    test*)
        echo "$file starts with 'test'"
        ;;
    *)
        echo "$file has unknown type"
        ;;
esac
Output:
script.sh is a shell script

Character classes [...] match any single character in the set. Common classes include: - [0-9] for digits - [a-z] for lowercase letters - [A-Z] for uppercase letters - [abc] for specific characters

Edit
#!/bin/sh
char="7"

case $char in
    [0-9])
        echo "'$char' is a digit"
        ;;
    [a-z])
        echo "'$char' is a lowercase letter"
        ;;
    [A-Z])
        echo "'$char' is an uppercase letter"
        ;;
    *)
        echo "'$char' is something else"
        ;;
esac
Output:
'7' is a digit

Case patterns are case-sensitive by default. Use | to list common variations like yes/Yes/YES. This is a common pattern for user input handling.

Edit
#!/bin/sh
response="Yes"

case $response in
    y | Y | yes | Yes | YES)
        echo "User said yes"
        ;;
    n | N | no | No | NO)
        echo "User said no"
        ;;
    *)
        echo "Unknown response: $response"
        ;;
esac
Output:
User said yes

Sometimes you want to explicitly do nothing for certain patterns. Use an empty block or just :. The : command is a shell builtin that does nothing.

Edit
#!/bin/sh
log_level="debug"

case $log_level in
    debug)
        # Intentionally do nothing for debug in production
        :
        ;;
    info)
        echo "Info: Application started"
        ;;
    error)
        echo "Error: Something went wrong"
        ;;
esac

echo "Logging complete"
Output:
Logging complete

Case statements are commonly used for subcommand dispatch. This pattern appears in init scripts and CLI tools that support commands like “start”, “stop”, “restart”.

Edit
#!/bin/sh
command="status"

case $command in
    start)
        echo "Starting service..."
        ;;
    stop)
        echo "Stopping service..."
        ;;
    restart)
        echo "Restarting service..."
        ;;
    status)
        echo "Service is running"
        ;;
    *)
        echo "Usage: start|stop|restart|status"
        ;;
esac
Output:
Service is running

Bash

Bash extends case with fall-through operators: - ;& falls through to the next block unconditionally - ;;& continues testing remaining patterns

These are Bash-specific and not POSIX-compliant.

Edit
#!/bin/bash
grade="B"

echo "Using ;& (unconditional fall-through):"
case $grade in
    A)
        echo "  Excellent!"
        ;&
    B)
        echo "  Good job!"
        ;&
    C)
        echo "  You passed."
        ;;
    *)
        echo "  Needs improvement."
        ;;
esac

echo ""
echo "Using ;;& (continue testing patterns):"
value="hello"
case $value in
    hello)
        echo "  Matched: hello"
        ;;&
    h*)
        echo "  Matched: h*"
        ;;&
    *o)
        echo "  Matched: *o"
        ;;
esac
Output:
Using ;& (unconditional fall-through):
  Good job!
  You passed.

Using ;;& (continue testing patterns):
  Matched: hello
  Matched: h*
  Matched: *o

« If Statements | For Loops »