The Linux Command Line (2012)
Part IV. Writing Shell Scripts
Chapter 31. Flow Control: Branching with case
In this chapter, we will continue to look at flow control. In Chapter 28, we constructed some simple menus and built the logic used to act on a user’s selection. To do this, we used a series of if commands to identify which of the possible choices had been selected. This type of construct appears frequently in programs, so much so that many programming languages (including the shell) provide a flow-control mechanism for multiple-choice decisions.
case
The bash multiple-choice compound command is called case. It has the following syntax:
case word in
[pattern [| pattern]...) commands ;;]...
esac
If we look at the read-menu program from Chapter 28, we see the logic used to act on a user’s selection
#!/bin/bash
# read-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] > "
if [[ $REPLY =˜ ^[0-3]$ ]]; then
if [[ $REPLY == 0 ]]; then
echo "Program terminated."
exit
fi
if [[ $REPLY == 1 ]]; then
echo "Hostname: $HOSTNAME"
uptime
exit
fi
if [[ $REPLY == 2 ]]; then
df -h
exit
fi
if [[ $REPLY == 3 ]]; then
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
exit
fi
else
echo "Invalid entry." >&2
exit 1
fi
Using case, we can replace this logic with something simpler:
#!/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
;;
esac
The case command looks at the value of word—in our example, the value of the REPLY variable—and then attempts to match it against one of 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.
Patterns
The patterns used by case are the same as those used by pathname expansion. Patterns are terminated with a ) character. Table 31-1 shows some valid patterns.
Table 31-1. case Pattern Examples
Pattern |
Description |
a) |
Matches if word equals a. |
[[:alpha:]]) |
Matches if word is a single alphabetic character. |
???) |
Matches if word is exactly three characters long. |
*.txt) |
Matches if word ends with the characters .txt. |
*) |
Matches any value of word. It is good practice to include this as the last pattern in a case command to catch any values of word that did not match a previous pattern; that is, to catch any possible invalid values. |
Here is an example of patterns at work:
#!/bin/bash
read -p "enter word > "
case $REPLY in
[[:alpha:]]) echo "is a single alphabetic character." ;;
[ABC][0-9]) echo "is A, B, or C followed by a digit." ;;
???) echo "is three characters long." ;;
*.txt) echo "is a word ending in '.txt'" ;;
*) echo "is something else." ;;
esac
Combining Multiple Patterns
It is also possible to combine multiple patterns using the vertical pipe character as a separator. This creates an “or” conditional pattern. This is useful for such things as handling both upper- and lowercase characters. For example:
#!/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
;;
esac
Here, we modify the case-menu program to use letters instead of digits for menu selection. Notice that the new patterns allow for entry of both upper- and lowercase letters.
Final Note
The case command is a handy addition to our bag of programming tricks. As we will see in the next chapter, it’s types of problems.