2. Branching with case
The command case
is a multiple-choice command.
-
Let's see an example that implements a menu program with
case
:case-menu.sh
#!/bin/bash
# case-menu: a menu driven system information program
clear
echo "
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
"
read -p "Enter selection [0-3] > "
case "$REPLY" in
0) echo "Program terminated."
exit
;;
1) echo "Hostname: $HOSTNAME"
uptime
;;
2) df -h .
;;
3) if [[ "$(id -u)" -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh "$HOME"
fi
;;
*) echo "Invalid entry" >&2
exit 1
;;
esacvim case-menu.sh
We have seen this example before, implemented with
if
and it is clear that withcase
it is much simpler../case-menu.sh
case
attempts a match against the specified patterns. When a match is found, the commands associated with the specified pattern are executed. After a match is found, no further matches are attempted. -
The patterns used by case are the same as those used by pathname expansion. For example:
a)
-- matches the character "a"[[:alpha:]])
-- matches any alphabetic character???)
-- matches 3 characters*.txt)
-- matches anything that ends in.txt
*)
-- matches anything
It is good practice to include
*)
as the last pattern in acase
command, to catch any values that did not match a previous pattern.Let's see an example script with patterns:
case-patterns.sh
#!/bin/bash
read -p "enter word > "
case "$REPLY" in
[[:alpha:]]) echo "it is a single alphabetic character" ;;
[ABC][0-9]) echo "it is A, B, or C followed by a digit" ;;
???) echo "it is three characters long" ;;
*.txt) echo "it is a word ending in '.txt'" ;;
*) echo "it is something else" ;;
esacvim case-patterns.sh
./case-patterns.sh # enter: x
./case-patterns.sh # enter: B2
./case-patterns.sh # enter: foo.txt
./case-patterns.sh # enter: xyz
./case-patterns.sh # enter: ab
-
It is also possible to combine multiple patterns using the vertical bar character as a separator. Let's see a modified menu program that uses letters instead of digits for menu selection:
case-menu-l
.sh#!/bin/bash
# case-menu: a menu driven system information program
clear
echo "
Please Select:
A. Display System Information
B. Display Disk Space
C. Display Home Space Utilization
Q. Quit
"
read -p "Enter selection [A, B, C or Q] > "
case "$REPLY" in
q|Q) echo "Program terminated."
exit
;;
a|A) echo "Hostname: $HOSTNAME"
uptime
;;
b|B) df -h .
;;
c|C) if [[ "$(id -u)" -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh "$HOME"
fi
;;
*) echo "Invalid entry" >&2
exit 1
;;
esacvim case-menu-l.sh
Notice how the new patterns allow for entry of both uppercase and lowercase letters.
./case-menu-l.sh
-
When a pattern is matched, the corresponding actions are executed, and
;;
makes sure that processing is stopped (without trying to match the following patterns). If we want instead to try matching them as well, we can use;;&
instead, as in this example:case4.sh
#!/bin/bash
# case4: test a character
# -n 1: read only one char and don't wait for enter to be pressed
read -n 1 -p "Type a character > "
echo
case "$REPLY" in
[[:upper:]]) echo "'$REPLY' is upper case." ;;&
[[:lower:]]) echo "'$REPLY' is lower case." ;;&
[[:alpha:]]) echo "'$REPLY' is alphabetic." ;;&
[[:digit:]]) echo "'$REPLY' is a digit." ;;&
[[:graph:]]) echo "'$REPLY' is a visible character." ;;&
[[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;&
[[:space:]]) echo "'$REPLY' is a whitespace character." ;;&
[[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;&
esacvim case4.sh
./case4.sh # enter: a
./case4.sh # enter: X
./case4.sh # enter: +