|

Linux CLI 63 ๐Ÿง case command in shell scripts

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

#!/bin/bash

read -p "Enter your favorite fruit (apple, banana, or orange): " fruit

case $fruit in
    apple)
        echo "You like apples!"
        ;;
    banana)
        echo "You like bananas!"
        ;;
    orange)
        echo "You like oranges!"
        ;;
    *)
        echo "I don't know that fruit."
        ;;
esac

read -p "Enter your favorite fruit: " fruit

case $fruit in
    apple|apricot)
        echo "You like apples or apricots!"
        ;;
    banana|banano*)
        echo "You like bananas or something that starts with 'banano'."
        ;;
    orange|[Oo]range)
        echo "You like oranges or maybe a misspelling of 'orange'."
        ;;
    *)
        echo "I don't know that fruit."
        ;;
esac

When you have one value to check against many possible patterns, a chain of if/elif/else gets ugly fast. The case statement is the cleaner alternative โ€” it matches a value against a list of patterns and runs the first block that matches.

Key point: case compares a single value against multiple patterns. Each pattern ends with ;;. The first match wins. The * pattern at the end is the default โ€” it catches anything that didn’t match earlier.


a – case command introduction

The case statement is used for conditional execution based on the value of a variable or expression. It provides a more readable and compact alternative to multiple if–elif–else statements when you have a limited set of possible values to check against.

Syntax:

case variable in
    pattern1)
        # commands if variable matches pattern1
        ;;
    pattern2)
        # commands if variable matches pattern2
        ;;
    pattern3)
        # commands if variable matches pattern3
        ;;
    *)
        # commands if none of the above patterns match
        ;;
esac

Breaking it down:

  1. variable is the value you want to check
  2. pattern1, pattern2, etc. are the values or patterns you want to match against
  3. ;; separates each pattern block
  4. * is a wildcard that matches any other case not explicitly listed

Basic example โ€” favorite fruit:

#!/bin/bash

read -p "Enter your favorite fruit (apple, banana, or orange): " fruit

case $fruit in
    apple)
        echo "You like apples!"
        ;;
    banana)
        echo "You like bananas!"
        ;;
    orange)
        echo "You like oranges!"
        ;;
    *)
        echo "I don't know that fruit."
        ;;
esac
$ ./fruit.sh
Enter your favorite fruit (apple, banana, or orange): apple
[ You like apples! ]

$ ./fruit.sh
Enter your favorite fruit (apple, banana, or orange): banana
[ You like bananas! ]

$ ./fruit.sh
Enter your favorite fruit (apple, banana, or orange): kiwi
[ I don't know that fruit. ]

How it flows:

  1. The case statement checks the value of fruit against several patterns (apple, banana, orange)
  2. If fruit matches one of these patterns, the corresponding block runs
  3. If none match, the * pattern’s block runs

case vs if/elif/else:

Aspectcaseif/elif/else
ChecksOne value against many patternsMany independent conditions
ReadabilityClean for many optionsGets verbose fast
Pattern matchingGlobs, classes, alternativesRegex or globs via [[ ]]
Fall-throughNo (each block is isolated)No
Default*)else
Best forMenus, options, dispatchComplex conditions

The same logic both ways:

# With if/elif/else
if [ "$fruit" = "apple" ]; then
    echo "You like apples!"
elif [ "$fruit" = "banana" ]; then
    echo "You like bananas!"
elif [ "$fruit" = "orange" ]; then
    echo "You like oranges!"
else
    echo "I don't know that fruit."
fi

# With case โ€” cleaner
case $fruit in
    apple)  echo "You like apples!" ;;
    banana) echo "You like bananas!" ;;
    orange) echo "You like oranges!" ;;
    *)      echo "I don't know that fruit." ;;
esac

For three options, if is fine. For ten, case wins hands-down.

The ;; terminator:

Each block ends with ;;. It’s like break in C’s switch โ€” it stops execution of the case after a match. Without it, the shell would keep checking the following patterns.

case $x in
    a) echo "A" ;;    # โ† the ;; ends this block
    b) echo "B" ;;
    c) echo "C" ;;
esac

Note: In bash, ;;& and ;& are alternatives that allow fall-through or continued testing, but ;; is what you’ll use 99% of the time.

Case is exact-match by default:

$ fruit="Apple"
$ case $fruit in
>     apple) echo "match" ;;
>     *) echo "no match" ;;
> esac
[ no match ]

Apple doesn’t match apple โ€” case-sensitive. Use a character class to be flexible:

case $fruit in
    [Aa]pple) echo "match" ;;
esac

Case with no default:

case $x in
    a) echo "A" ;;
    b) echo "B" ;;
esac

If x is neither a nor b, nothing happens. This is fine โ€” but often you want a default to catch typos.

A practical example โ€” service control:

#!/bin/bash

case "$1" in
    start)
        echo "Starting service..."
        # systemctl start myservice
        ;;
    stop)
        echo "Stopping service..."
        # systemctl stop myservice
        ;;
    restart)
        echo "Restarting service..."
        # systemctl restart myservice
        ;;
    status)
        echo "Checking status..."
        # systemctl status myservice
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}" >&2
        exit 1
        ;;
esac

This is the classic pattern for command-line tools.

A menu with case:

#!/bin/bash

while true; do
    echo "1) Show date"
    echo "2) Show uptime"
    echo "3) Show disk"
    echo "4) Quit"
    read -p "Choice: " choice

    case "$choice" in
        1) date ;;
        2) uptime ;;
        3) df -h ;;
        4) echo "Goodbye"; break ;;
        *) echo "Invalid choice" ;;
    esac
done

b – case command and patterns

case supports several kinds of patterns โ€” not just literal strings. This is where it becomes more powerful than a chain of [ "$x" = "y" ] tests.

Common pattern types:

  1. Simple strings
  2. Wildcards
  3. Character classes
  4. Alternatives
  5. Default

1. Simple strings:

case "$fruit" in
    apple)  echo "apple" ;;
    banana) echo "banana" ;;
esac

Exact match, case-sensitive.

2. Wildcards:

Use * to match any sequence of characters, and ? to match any single character:

case "$fruit" in
    a*)       echo "Starts with a" ;;
    *e)       echo "Ends with e" ;;
    ?????)    echo "Exactly five characters" ;;
    *)        echo "Anything else" ;;
esac
WildcardMeaning
*Any sequence (including empty)
?Any single character
[abc]Any one of a, b, c
[a-z]Any character in range
[!abc]Any character NOT in the set

3. Character classes:

POSIX classes work inside [ ... ]:

case "$input" in
    [[:digit:]]*)  echo "Starts with a digit" ;;
    [[:upper:]]*)  echo "Starts with uppercase" ;;
    [[:lower:]]*)  echo "Starts with lowercase" ;;
    [[:space:]]*)  echo "Starts with whitespace" ;;
esac

Available classes:

ClassMatches
[[:upper:]]Uppercase letters
[[:lower:]]Lowercase letters
[[:alpha:]]Letters
[[:digit:]]Digits
[[:alnum:]]Letters and digits
[[:space:]]Whitespace
[[:punct:]]Punctuation
[[:graph:]]Printable, non-space
[[:xdigit:]]Hex digits

4. Alternatives with |:

Match multiple patterns in one block:

case "$fruit" in
    apple|apricot)
        echo "You like apples or apricots!"
        ;;
    banana|banano*)
        echo "You like bananas or something that starts with 'banano'."
        ;;
    orange|[Oo]range)
        echo "You like oranges or maybe a misspelling of 'orange'."
        ;;
    *)
        echo "I don't know that fruit."
        ;;
esac

The full example from the top of the chapter:

#!/bin/bash

read -p "Enter your favorite fruit: " fruit

case $fruit in
    apple|apricot)
        echo "You like apples or apricots!"
        ;;
    banana|banano*)
        echo "You like bananas or something that starts with 'banano'."
        ;;
    orange|[Oo]range)
        echo "You like oranges or maybe a misspelling of 'orange'."
        ;;
    *)
        echo "I don't know that fruit."
        ;;
esac
$ ./fruit.sh
Enter your favorite fruit: apple
[ You like apples or apricots! ]

$ ./fruit.sh
Enter your favorite fruit: apricot
[ You like apples or apricots! ]

$ ./fruit.sh
Enter your favorite fruit: banana
[ You like bananas or something that starts with 'banano'. ]

$ ./fruit.sh
Enter your favorite fruit: bananofoo
[ You like bananas or something that starts with 'banano'. ]

$ ./fruit.sh
Enter your favorite fruit: orange
[ You like oranges or maybe a misspelling of 'orange'. ]

$ ./fruit.sh
Enter your favorite fruit: Orange
[ You like oranges or maybe a misspelling of 'orange'. ]

$ ./fruit.sh
Enter your favorite fruit: kiwi
[ I don't know that fruit. ]

Each block combines:

  • Literal alternatives โ€” apple|apricot
  • Wildcards โ€” banano*
  • Character classes โ€” [Oo]range

5. The default pattern *:

The final *) catches anything that didn’t match earlier:

case "$x" in
    1) echo "one" ;;
    2) echo "two" ;;
    *) echo "something else" ;;   # default
esac

Without *), unmatched values just fall through silently:

$ x=99
$ case "$x" in
>     1) echo "one" ;;
>     2) echo "two" ;;
> esac
$ echo $?
[ 0 ]

Nothing printed โ€” no error. Adding *) makes the behavior explicit.

Tip: Always include a *) default unless you have a specific reason not to. It catches typos and gives you a place to show a usage message.

Putting it all together โ€” a dispatch script:

#!/bin/bash

case "$1" in
    start|up|begin)
        echo "Starting..."
        ;;
    stop|down|halt)
        echo "Stopping..."
        ;;
    restart|reload)
        echo "Restarting..."
        ;;
    status|info)
        echo "Status..."
        ;;
    *.txt)
        echo "Text file: $1"
        ;;
    *.log)
        echo "Log file: $1"
        ;;
    [0-9]*)
        echo "Starts with a digit: $1"
        ;;
    "")
        echo "No argument given" >&2
        exit 1
        ;;
    *)
        echo "Unknown command: $1" >&2
        exit 1
        ;;
esac
$ ./dispatch.sh start
[ Starting... ]

$ ./dispatch.sh restart
[ Restarting... ]

$ ./dispatch.sh report.txt
[ Text file: report.txt ]

$ ./dispatch.sh 42things
[ Starts with a digit: 42things ]

$ ./dispatch.sh
[ No argument given ]

$ ./dispatch.sh frobnicate
[ Unknown command: frobnicate ]

Case with fall-through using ;;& (bash 4+):

Normally ;; stops after the first match. ;;& continues testing the following patterns โ€” useful for multiple matches:

$ x="apple"
$ case "$x" in
>     a*)     echo "starts with a" ;;&
>     *e)     echo "ends with e" ;;&
>     apple)  echo "is apple" ;;
> esac
[ starts with a ]
[ ends with e ]
[ is apple ]

And ;& falls through to the next block without testing:

$ case "a" in
>     a) echo "A" ;&
>     b) echo "B" ;;
> esac
[ A ]
[ B ]

These are bash-specific โ€” not POSIX. Use them only when you need fall-through behavior.

Case with a variable pattern:

$ pattern="app*"
$ case "apple" in
>     $pattern) echo "match" ;;
> esac
[ match ]

The pattern can come from a variable. This is useful for dynamic dispatch.

Case in a function:

check_os() {
    case "$(uname -s)" in
        Linux*)   echo "Linux" ;;
        Darwin*)  echo "macOS" ;;
        CYGWIN*)  echo "Cygwin" ;;
        MINGW*)   echo "MinGW" ;;
        *)        echo "Unknown" ;;
    esac
}
$ check_os
[ Linux ]

A common real-world use โ€” file type detection:

for file in *; do
    case "$file" in
        *.txt)        echo "$file: text" ;;
        *.jpg|*.png)  echo "$file: image" ;;
        *.sh)         echo "$file: script" ;;
        *.tar.gz)     echo "$file: archive" ;;
        *)            echo "$file: unknown" ;;
    esac
done

Complete Example Session

# ============================================
# PART 1: BASIC CASE
# ============================================

$ cat > fruit.sh << 'EOF'
#!/bin/bash
read -p "Enter your favorite fruit (apple, banana, or orange): " fruit

case $fruit in
    apple)
        echo "You like apples!"
        ;;
    banana)
        echo "You like bananas!"
        ;;
    orange)
        echo "You like oranges!"
        ;;
    *)
        echo "I don't know that fruit."
        ;;
esac
EOF

$ chmod +x fruit.sh
$ ./fruit.sh
Enter your favorite fruit (apple, banana, or orange): apple
[ You like apples! ]

$ ./fruit.sh
Enter your favorite fruit (apple, banana, or orange): banana
[ You like bananas! ]

$ ./fruit.sh
Enter your favorite fruit (apple, banana, or orange): kiwi
[ I don't know that fruit. ]

# ============================================
# PART 2: CASE WITH PATTERNS
# ============================================

$ cat > fruit2.sh << 'EOF'
#!/bin/bash
read -p "Enter your favorite fruit: " fruit

case $fruit in
    apple|apricot)
        echo "You like apples or apricots!"
        ;;
    banana|banano*)
        echo "You like bananas or something that starts with 'banano'."
        ;;
    orange|[Oo]range)
        echo "You like oranges or maybe a misspelling of 'orange'."
        ;;
    *)
        echo "I don't know that fruit."
        ;;
esac
EOF

$ chmod +x fruit2.sh
$ ./fruit2.sh
Enter your favorite fruit: apricot
[ You like apples or apricots! ]

$ ./fruit2.sh
Enter your favorite fruit: bananofoo
[ You like bananas or something that starts with 'banano'. ]

$ ./fruit2.sh
Enter your favorite fruit: Orange
[ You like oranges or maybe a misspelling of 'orange'. ]

# ============================================
# PART 3: SERVICE CONTROL
# ============================================

$ cat > service.sh << 'EOF'
#!/bin/bash
case "$1" in
    start)   echo "Starting..." ;;
    stop)    echo "Stopping..." ;;
    restart) echo "Restarting..." ;;
    status)  echo "Status..." ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}" >&2
        exit 1
        ;;
esac
EOF

$ chmod +x service.sh
$ ./service.sh start
[ Starting... ]
$ ./service.sh restart
[ Restarting... ]
$ ./service.sh frobnicate
[ Usage: ./service.sh {start|stop|restart|status} ]

# ============================================
# PART 4: CHARACTER CLASSES
# ============================================

$ for x in 1abc ABCdef abc123 "  spaced" "!hello"; do
>     case "$x" in
>         [[:digit:]]*) echo "$x โ†’ starts with digit" ;;
>         [[:upper:]]*) echo "$x โ†’ starts with uppercase" ;;
>         [[:lower:]]*) echo "$x โ†’ starts with lowercase" ;;
>         [[:space:]]*) echo "$x โ†’ starts with space" ;;
>         *)            echo "$x โ†’ other" ;;
>     esac
> done
[ 1abc โ†’ starts with digit ]
[ ABCdef โ†’ starts with uppercase ]
[ abc123 โ†’ starts with lowercase ]
[   spaced โ†’ starts with space ]
[ !hello โ†’ other ]

# ============================================
# PART 5: FILE TYPE DETECTION
# ============================================

$ for file in report.txt photo.jpg script.sh archive.tar.gz data.bin; do
>     case "$file" in
>         *.txt)         echo "$file: text" ;;
>         *.jpg|*.png)   echo "$file: image" ;;
>         *.sh)          echo "$file: script" ;;
>         *.tar.gz)      echo "$file: archive" ;;
>         *)             echo "$file: unknown" ;;
>     esac
> done
[ report.txt: text ]
[ photo.jpg: image ]
[ script.sh: script ]
[ archive.tar.gz: archive ]
[ data.bin: unknown ]

# ============================================
# PART 6: MENU WITH CASE
# ============================================

$ cat > menu.sh << 'EOF'
#!/bin/bash
while true; do
    echo "1) Date"
    echo "2) Uptime"
    echo "3) Disk"
    echo "4) Quit"
    read -p "Choice: " c
    case "$c" in
        1) date ;;
        2) uptime ;;
        3) df -h / ;;
        4) echo "Bye"; break ;;
        *) echo "Invalid choice" ;;
    esac
    echo
done
EOF

$ chmod +x menu.sh
$ ./menu.sh
[ 1) Date ]
[ 2) Uptime ]
[ 3) Disk ]
[ 4) Quit ]
Choice: 1
[ Mon Jan 15 10:00:00 UTC 2024 ]
[ 1) Date ]
[ 2) Uptime ]
[ 3) Disk ]
[ 4) Quit ]
Choice: 4
[ Bye ]

# ============================================
# PART 7: OS DETECTION
# ============================================

$ cat > os.sh << 'EOF'
#!/bin/bash
case "$(uname -s)" in
    Linux*)   echo "Linux" ;;
    Darwin*)  echo "macOS" ;;
    CYGWIN*)  echo "Cygwin" ;;
    MINGW*)   echo "MinGW" ;;
    *)        echo "Unknown" ;;
esac
EOF

$ chmod +x os.sh
$ ./os.sh
[ Linux ]

# ============================================
# PART 8: YES/NO CONFIRMATION
# ============================================

$ read -p "Continue? (y/n): " ans
Continue? (y/n): y
$ case "$ans" in
>     [Yy]|[Yy]es|YES) echo "Continuing" ;;
>     [Nn]|[Nn]o|NO)   echo "Cancelled" ;;
>     *)               echo "Please answer y or n" ;;
> esac
[ Continuing ]

# ============================================
# PART 9: CASE WITH FALL-THROUGH (bash 4+)
# ============================================

$ x="apple"
$ case "$x" in
>     a*)     echo "starts with a" ;;&
>     *e)     echo "ends with e" ;;&
>     apple)  echo "is apple" ;;
> esac
[ starts with a ]
[ ends with e ]
[ is apple ]

# ============================================
# PART 10: EMPTY INPUT
# ============================================

$ read -p "Name: " name
Name:
$ case "$name" in
>     "")   echo "Empty input" ;;
>     *)    echo "Hello, $name" ;;
> esac
[ Empty input ]

# ============================================
# PART 11: COMBINING PATTERNS
# ============================================

$ for x in a b c d e; do
>     case "$x" in
>         a|b|c) echo "$x: in first group" ;;
>         d|e)   echo "$x: in second group" ;;
>         *)     echo "$x: other" ;;
>     esac
> done
[ a: in first group ]
[ b: in first group ]
[ c: in first group ]
[ d: in second group ]
[ e: in second group ]

# ============================================
# PART 12: FULL EXAMPLE
# ============================================

$ cat > full.sh << 'EOF'
#!/bin/bash

read -p "Enter your favorite fruit (apple, banana, or orange): " fruit

case $fruit in
    apple)
        echo "You like apples!"
        ;;
    banana)
        echo "You like bananas!"
        ;;
    orange)
        echo "You like oranges!"
        ;;
    *)
        echo "I don't know that fruit."
        ;;
esac

read -p "Enter your favorite fruit: " fruit

case $fruit in
    apple|apricot)
        echo "You like apples or apricots!"
        ;;
    banana|banano*)
        echo "You like bananas or something that starts with 'banano'."
        ;;
    orange|[Oo]range)
        echo "You like oranges or maybe a misspelling of 'orange'."
        ;;
    *)
        echo "I don't know that fruit."
        ;;
esac
EOF

$ chmod +x full.sh
$ ./full.sh
Enter your favorite fruit (apple, banana, or orange): apple
[ You like apples! ]
Enter your favorite fruit: apricot
[ You like apples or apricots! ]

Quick Reference

case Syntax

PartMeaning
case $var inStart the case
pattern)Start a block
;;End a block
*)Default
esacEnd the case

Pattern Types

PatternMatches
appleLiteral string
a*Starts with a
*eEnds with e
?????Exactly 5 chars
[abc]Any of a, b, c
[a-z]Any lowercase
[!abc]Not a, b, c
apple|apricotEither
*Anything

Character Classes

ClassMatches
[[:upper:]]Uppercase
[[:lower:]]Lowercase
[[:alpha:]]Letters
[[:digit:]]Digits
[[:alnum:]]Letters + digits
[[:space:]]Whitespace
[[:punct:]]Punctuation
[[:xdigit:]]Hex digits

Terminators

TerminatorEffect
;;Stop after match
;&Fall through to next block (bash)
;;&Continue testing patterns (bash)

case vs if

Use case whenUse if when
One value, many patternsMany independent conditions
Matching strings, globs, classesComparing numbers, files
Menu / dispatchComplex boolean logic
Patterns like *.txtRegex via [[ =~ ]]

Best Practices

โœ… Do This:

# Always include a default
case "$x" in
    a) echo "A" ;;
    *) echo "Unknown" ;;
esac                                # โœ…

# Quote the variable
case "$x" in ... esac               # โœ…

# Use alternatives for related patterns
case "$x" in
    start|up|begin) echo "Starting" ;;
esac                                # โœ…

# Use | for case-insensitive yes/no
case "$ans" in
    [Yy]|[Yy]es) echo "yes" ;;
    [Nn]|[Nn]o)  echo "no" ;;
esac                                # โœ…

# Use for file extension dispatch
case "$file" in
    *.txt)  echo "text" ;;
    *.jpg)  echo "image" ;;
esac                                # โœ…

# Show usage on bad input
case "$1" in
    start) ... ;;
    *) echo "Usage: $0 {start}" >&2; exit 1 ;;
esac                                # โœ…

# Use a variable for dynamic patterns
case "$x" in
    $pattern) echo "match" ;;
esac                                # โœ…

โŒ Don’t Do This:

# Don't forget the ;;
case "$x" in
    a) echo "A"
    b) echo "B"                     # โŒ syntax error
esac

# Don't forget esac
case "$x" in
    a) echo "A" ;;
                                    # โŒ missing esac

# Don't use case for numeric comparisons
case "$n" in
    1) echo "one" ;;
    >5) echo "big"                  # โŒ no > in patterns
esac

# Don't rely on case being case-insensitive
case "$x" in
    apple) ...                      # โš ๏ธ  won't match "Apple"
esac

# Don't forget the pipe for alternatives
case "$x" in
    start stop) ...                 # โŒ matches literal "start stop"
    start|stop) ...                 # โœ…

# Don't omit default on user input
case "$input" in
    yes) ... ;;
esac                                # โš ๏ธ  typo goes unnoticed

# Don't use bash-only features in sh
#!/bin/sh
case "$x" in
    a) echo "A" ;;&                 # โŒ not POSIX
esac

Common Pitfalls

PitfallProblemSolution
Missing ;;Syntax errorAdd terminator
Missing esacSyntax errorAdd closing keyword
Unquoted variableWord splittingQuote: "$x"
Case-sensitive“Apple” โ‰  “apple”Use [Aa]pple
No defaultSilent on no matchAdd *)
> in patternsNot supportedUse [[ ]] or if
Literal space in patternMatches spaceWatch whitespace
Forgot |Pattern literalUse a|b

Real-World Examples

1. Basic Fruit Match

#!/bin/bash
read -p "Enter your favorite fruit (apple, banana, or orange): " fruit
case $fruit in
    apple)
        echo "You like apples!"
        ;;
    banana)
        echo "You like bananas!"
        ;;
    orange)
        echo "You like oranges!"
        ;;
    *)
        echo "I don't know that fruit."
        ;;
esac

2. Fruit with Alternatives

#!/bin/bash
read -p "Enter your favorite fruit: " fruit
case $fruit in
    apple|apricot)
        echo "You like apples or apricots!"
        ;;
    banana|banano*)
        echo "You like bananas or something that starts with 'banano'."
        ;;
    orange|[Oo]range)
        echo "You like oranges or maybe a misspelling of 'orange'."
        ;;
    *)
        echo "I don't know that fruit."
        ;;
esac

3. Service Control

#!/bin/bash
case "$1" in
    start)   echo "Starting service..." ;;
    stop)    echo "Stopping service..." ;;
    restart) echo "Restarting service..." ;;
    status)  echo "Checking status..." ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}" >&2
        exit 1
        ;;
esac

4. Yes/No Confirmation

#!/bin/bash
read -p "Continue? (y/n): " ans
case "$ans" in
    [Yy]|[Yy]es) echo "Continuing" ;;
    [Nn]|[Nn]o)  echo "Cancelled" ;;
    *)           echo "Please answer y or n" ;;
esac

5. File Extension

#!/bin/bash
for file in *; do
    case "$file" in
        *.txt)        echo "$file: text" ;;
        *.jpg|*.png)  echo "$file: image" ;;
        *.sh)         echo "$file: script" ;;
        *.tar.gz)     echo "$file: archive" ;;
        *)            echo "$file: unknown" ;;
    esac
done

6. Menu

#!/bin/bash
while true; do
    echo "1) Date"
    echo "2) Uptime"
    echo "3) Quit"
    read -p "Choice: " c
    case "$c" in
        1) date ;;
        2) uptime ;;
        3) break ;;
        *) echo "Invalid" ;;
    esac
done

7. OS Detection

#!/bin/bash
case "$(uname -s)" in
    Linux*)   echo "Linux" ;;
    Darwin*)  echo "macOS" ;;
    CYGWIN*)  echo "Cygwin" ;;
    MINGW*)   echo "MinGW" ;;
    *)        echo "Unknown" ;;
esac

8. Handle Empty Input

#!/bin/bash
read -p "Name: " name
case "$name" in
    "")  echo "Empty input" ;;
    *)   echo "Hello, $name" ;;
esac

9. Log Level Filter

#!/bin/bash
level="$1"
case "$level" in
    debug|DEBUG)  echo "Debug mode" ;;
    info|INFO)    echo "Info mode" ;;
    warn|WARN)    echo "Warning mode" ;;
    error|ERROR)  echo "Error mode" ;;
    *)            echo "Unknown level" >&2; exit 1 ;;
esac

10. Extension Routing

#!/bin/bash
file="$1"
case "$file" in
    *.tar.gz|*.tgz)  tar -xzf "$file" ;;
    *.tar.bz2)       tar -xjf "$file" ;;
    *.zip)           unzip "$file" ;;
    *.gz)            gunzip "$file" ;;
    *)               echo "Unknown archive: $file" >&2; exit 1 ;;
esac

11. Version Check

#!/bin/bash
case "$(uname -r)" in
    5.*) echo "Modern kernel" ;;
    4.*) echo "Older kernel" ;;
    *)   echo "Ancient kernel" ;;
esac

12. Git Hook

#!/bin/bash
case "$1" in
    commit)  echo "Running pre-commit checks..." ;;
    push)    echo "Running pre-push checks..." ;;
    *)       echo "Unknown hook: $1" ;;
esac

13. Colors

#!/bin/bash
color="$1"
case "$color" in
    red)    echo -e "\033[31mRed\033[0m" ;;
    green)  echo -e "\033[32mGreen\033[0m" ;;
    blue)   echo -e "\033[34mBlue\033[0m" ;;
    *)      echo "Unknown color" ;;
esac

14. Filename Classification

#!/bin/bash
for f in *; do
    case "$f" in
        .*)           echo "$f: hidden" ;;
        *.bak)        echo "$f: backup" ;;
        *~)           echo "$f: temp" ;;
        *.log)        echo "$f: log" ;;
        *)            echo "$f: regular" ;;
    esac
done

15. Cleanup by Type

#!/bin/bash
for f in *; do
    case "$f" in
        *.tmp|*.swp|*~)
            echo "Removing $f"
            rm -f "$f"
            ;;
    esac
done

16. Parse Command-Line Option

#!/bin/bash
case "$1" in
    -h|--help)     echo "Help text" ;;
    -v|--version)  echo "Version 1.0" ;;
    -d|--debug)    set -x ;;
    *)             echo "Unknown option" ;;
esac

17. Prompt Loop Until Valid

#!/bin/bash
while true; do
    read -p "Choose (a/b/c): " c
    case "$c" in
        a|A) echo "You chose A"; break ;;
        b|B) echo "You chose B"; break ;;
        c|C) echo "You chose C"; break ;;
        *)   echo "Invalid, try again" ;;
    esac
done

18. Case in a Function

#!/bin/bash
http_status() {
    case "$1" in
        200) echo "OK" ;;
        301) echo "Moved Permanently" ;;
        404) echo "Not Found" ;;
        500) echo "Internal Server Error" ;;
        *)   echo "Unknown status" ;;
    esac
}
http_status 200
http_status 404
[ OK ]
[ Not Found ]

19. Bash Fall-Through

#!/bin/bash
x="apple"
case "$x" in
    a*)     echo "starts with a" ;;&
    *e)     echo "ends with e" ;;&
    apple)  echo "is apple" ;;
esac
[ starts with a ]
[ ends with e ]
[ is apple ]

20. Full Combined Example

#!/bin/bash

read -p "Enter your favorite fruit (apple, banana, or orange): " fruit

case $fruit in
    apple)
        echo "You like apples!"
        ;;
    banana)
        echo "You like bananas!"
        ;;
    orange)
        echo "You like oranges!"
        ;;
    *)
        echo "I don't know that fruit."
        ;;
esac

read -p "Enter your favorite fruit: " fruit

case $fruit in
    apple|apricot)
        echo "You like apples or apricots!"
        ;;
    banana|banano*)
        echo "You like bananas or something that starts with 'banano'."
        ;;
    orange|[Oo]range)
        echo "You like oranges or maybe a misspelling of 'orange'."
        ;;
    *)
        echo "I don't know that fruit."
        ;;
esac

Visual: How case Works

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚           case $fruit in                     โ”‚
โ”‚                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚  "apple"                               โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                   โ”‚                          โ”‚
โ”‚          match? โ”€โ”€โ”ดโ”€โ”€ no โ”€โ”€โ”                 โ”‚
โ”‚           โ”‚                โ”‚                 โ”‚
โ”‚          yes               โ–ผ                 โ”‚
โ”‚           โ”‚        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚           โ”‚        โ”‚  "banana"          โ”‚    โ”‚
โ”‚           โ”‚        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚           โ”‚                  โ”‚               โ”‚
โ”‚           โ”‚         match? โ”€โ”€โ”ดโ”€โ”€ no โ”€โ”€โ”      โ”‚
โ”‚           โ”‚          โ”‚                โ”‚      โ”‚
โ”‚           โ”‚         yes               โ–ผ      โ”‚
โ”‚           โ”‚          โ”‚        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚           โ”‚          โ”‚        โ”‚  "orange"  โ”‚ โ”‚
โ”‚           โ”‚          โ”‚        โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚           โ”‚          โ”‚              โ”‚        โ”‚
โ”‚           โ”‚          โ”‚     match? โ”€โ”€โ”ดโ”€ no โ”€โ” โ”‚
โ”‚           โ”‚          โ”‚       โ”‚             โ”‚ โ”‚
โ”‚           โ”‚          โ”‚      yes            โ–ผ โ”‚
โ”‚           โ”‚          โ”‚       โ”‚      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚
โ”‚           โ”‚          โ”‚       โ”‚      โ”‚  *    โ”‚โ”‚
โ”‚           โ”‚          โ”‚       โ”‚      โ””โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”˜โ”‚
โ”‚           โ”‚          โ”‚       โ”‚          โ”‚    โ”‚
โ”‚           โ–ผ          โ–ผ       โ–ผ          โ–ผ    โ”‚
โ”‚       run apple   run banana  run orange  runโ”‚
โ”‚       block       block       block      defaultโ”‚
โ”‚                                              โ”‚
โ”‚  esac                                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Pattern Types

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚           Pattern examples                   โ”‚
โ”‚                                              โ”‚
โ”‚  apple          โ†’ exact match                โ”‚
โ”‚  a*             โ†’ starts with a              โ”‚
โ”‚  *e             โ†’ ends with e                โ”‚
โ”‚  ????           โ†’ 4 characters               โ”‚
โ”‚  [abc]          โ†’ a, b, or c                 โ”‚
โ”‚  [a-z]          โ†’ lowercase                  โ”‚
โ”‚  [!0-9]         โ†’ not a digit                โ”‚
โ”‚  apple|apricot  โ†’ either                     โ”‚
โ”‚  [[:digit:]]*   โ†’ starts with digit          โ”‚
โ”‚  [[:upper:]]*   โ†’ starts with uppercase      โ”‚
โ”‚  *)             โ†’ default (catch-all)        โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ConceptSyntaxExample
Basic casecase $x in ... esaccase $fruit in
Pattern blockpattern) ... ;;apple) echo "A" ;;
Default*)*) echo "Unknown" ;;
Alternativesa|bapple|apricot)
Wildcards* ?a*), ?????
Char class[...][Oo]range
POSIX class[[:digit:]][[:digit:]]*)
Negation[!...][!0-9]
Fall-through;&a) echo A ;&
Continue testing;;&a*) echo ;;&

Key takeaways:

  • case matches one value against many patterns โ€” cleaner than a long if/elif chain
  • Each block ends with ;;
  • The whole statement ends with esac (case backwards)
  • Use * as the default to catch unmatched values โ€” always include it
  • Patterns support: literal strings, wildcards (*, ?), character classes ([abc], [a-z]), POSIX classes ([[:digit:]]), and alternatives (a|b)
  • Case is case-sensitive by default โ€” use [Aa]pple or [Yy] for flexibility
  • case is perfect for: menus, service control, yes/no prompts, file extension dispatch, OS detection, command-line parsing
  • No fall-through by default โ€” each match runs its block and stops
  • Bash 4+ adds ;& and ;;& for fall-through and continued testing โ€” but they’re bash-only
  • Don’t use case for numeric ranges โ€” that’s for if/[[ ]] or (( ))

Remember: case is the clean way to branch on a single value. Whenever you catch yourself writing if [ "$x" = "a" ] ... elif [ "$x" = "b" ] ... elif ..., reach for case instead. It handles patterns, alternatives, and defaults in a readable, compact form. Always include a *) default to catch the unexpected, and remember that case is case-sensitive โ€” use character classes to be flexible. Master case, and your dispatch logic becomes clean and obvious.

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!