Case Statements
The case statement provides an elegant way to handle multiple conditions, especially when comparing a single variable against many possible values.
Basic Case Syntax
case $VARIABLE in
pattern1)
commands
;;
pattern2)
commands
;;
*)
default_commands
;;
esac
Each pattern ends with ), commands end with ;;, and the whole block ends with esac (case backwards).
Simple Example
#!/bin/bash
read -p "Enter a fruit: " FRUIT
case $FRUIT in
apple)
echo "Apples are red or green"
;;
banana)
echo "Bananas are yellow"
;;
orange)
echo "Oranges are orange"
;;
*)
echo "Unknown fruit"
;;
esac
Case vs If-Elif
Case is cleaner for multiple value checks:
# Using if-elif (verbose)
if [ "$CMD" = "start" ]; then
echo "Starting"
elif [ "$CMD" = "stop" ]; then
echo "Stopping"
elif [ "$CMD" = "restart" ]; then
echo "Restarting"
else
echo "Unknown command"
fi
# Using case (cleaner)
case $CMD in
start) echo "Starting" ;;
stop) echo "Stopping" ;;
restart) echo "Restarting" ;;
*) echo "Unknown command" ;;
esac
Pattern Matching in Case
Case supports glob patterns:
#!/bin/bash
FILE="document.txt"
case $FILE in
*.txt)
echo "Text file"
;;
*.sh)
echo "Shell script"
;;
*.jpg|*.png|*.gif)
echo "Image file"
;;
*)
echo "Unknown type"
;;
esac
Pattern Syntax
| Pattern | Matches |
|---|---|
* | Any string |
? | Any single character |
[abc] | a, b, or c |
[a-z] | Any lowercase letter |
[!abc] | Anything except a, b, c |
pat1|pat2 | pat1 OR pat2 |
Exercise: File Type Detection
Use case to check file type:
Multiple Patterns
Match multiple patterns for the same action:
#!/bin/bash
case $ANSWER in
y|Y|yes|Yes|YES)
echo "You said yes"
;;
n|N|no|No|NO)
echo "You said no"
;;
*)
echo "Please answer yes or no"
;;
esac
You can also use character classes:
case $ANSWER in
[yY]|[yY][eE][sS])
echo "You said yes"
;;
[nN]|[nN][oO])
echo "You said no"
;;
esac
Case Statement for Options
Common pattern for processing command-line options:
#!/bin/bash
# Simple service script
case $1 in
start)
echo "Starting service..."
# start commands here
;;
stop)
echo "Stopping service..."
# stop commands here
;;
restart)
echo "Restarting service..."
# restart commands here
;;
status)
echo "Checking status..."
# status commands here
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
Fall-Through with ;;&
In bash 4+, use ;;& to continue testing patterns:
#!/bin/bash
NUM=15
case $NUM in
*5)
echo "Ends with 5"
;;& # Continue checking
1?)
echo "Is a teen number"
;;& # Continue checking
*)
echo "Processing complete"
;;
esac
# Output:
# Ends with 5
# Is a teen number
# Processing complete
Continue with ;&
Use ;& to fall through to the next case without testing:
#!/bin/bash
LEVEL=2
case $LEVEL in
1)
echo "Basic features"
;& # Fall through
2)
echo "Advanced features"
;& # Fall through
3)
echo "Premium features"
;;
esac
# If LEVEL=2, outputs:
# Advanced features
# Premium features
Exercise: Menu System
Create a simple menu:
Nested Case Statements
Case statements can be nested:
#!/bin/bash
TYPE="text"
FORMAT="html"
case $TYPE in
text)
case $FORMAT in
html) echo "Converting to HTML" ;;
pdf) echo "Converting to PDF" ;;
*) echo "Unknown format" ;;
esac
;;
image)
case $FORMAT in
jpg) echo "Saving as JPEG" ;;
png) echo "Saving as PNG" ;;
*) echo "Unknown format" ;;
esac
;;
esac
Real-World Example: Argument Parser
#!/bin/bash
# Parse command line arguments
while [ $# -gt 0 ]; do
case $1 in
-h|--help)
echo "Usage: $0 [-v] [-f file] [-o output]"
exit 0
;;
-v|--verbose)
VERBOSE=true
shift
;;
-f|--file)
INPUT_FILE="$2"
shift 2
;;
-o|--output)
OUTPUT_FILE="$2"
shift 2
;;
-*)
echo "Unknown option: $1"
exit 1
;;
*)
ARGS+=("$1")
shift
;;
esac
done
echo "Verbose: $VERBOSE"
echo "Input: $INPUT_FILE"
echo "Output: $OUTPUT_FILE"
echo "Args: ${ARGS[*]}"
HTTP Status Code Handler
#!/bin/bash
STATUS=404
case $STATUS in
200)
echo "OK"
;;
301|302)
echo "Redirect"
;;
400)
echo "Bad Request"
;;
401|403)
echo "Auth Error"
;;
404)
echo "Not Found"
;;
5??)
echo "Server Error"
;;
*)
echo "Unknown status: $STATUS"
;;
esac
Best Practices
- Always include a default case (
*) to handle unexpected input - Use patterns for flexibility -
*.txtis better than listing all filenames - Group related patterns with
|to reduce duplication - Keep actions short - call functions for complex logic
- Align your cases for readability
# Good formatting
case $VAR in
pattern1) simple_action ;;
pattern2)
complex_action
multiple_lines
;;
*)
default_action
;;
esac
Key Takeaways
- Case statements match a variable against multiple patterns
- Each pattern ends with
), each action ends with;; - Use
*as a default/catch-all pattern - Patterns support globs:
*,?,[...] - Use
|to match multiple patterns:yes|Yes|YES) - Case is more readable than multiple if-elif statements
;;ends the case,;&falls through,;;&continues testing- Always end with
esac

