case 分支在本章中,我们将继续探讨流量控制。在【第28章.阅读键盘输入】中,我们构建了一些简单的菜单,并构建了用于对用户选择进行操作的逻辑。为此,我们使用了一系列 if 命令来识别选择了哪些可能的选项。这种逻辑结构在程序中经常出现,以至于许多编程语言(包括shell)为多项选择决策提供了一种特殊的流控制机制。
case 在bash中,多选复合命令称为 case 。它具有以下语法:
case word in  [pattern [| *pattern]...) commands ;;]...  esac
如果我们查看【第28章】中的读取菜单程序,我们会看到用于对用户选择进行操作的逻辑。
xxxxxxxxxx# 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 -r -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使用 case ,我们可以用更简单的东西替换这个逻辑。
xxxxxxxxxx# case-menu: a menu driven system information program clear echo " Please Select: 1. Display System Information 2. Display Disk Space 3. Display Home Space Utilization0. Quit " read -r -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         ;;esaccase 命令查看 word 的值,在我们的示例中是 REPLY 变量的值,然后尝试将其与指定的模式(patterns)之一进行匹配。当找到匹配项时,将执行与指定模式关联的命令(commands)。找到匹配后,不再尝试进一步的匹配。
patterns —— 模式
case 使用的模式与路径名扩展使用的模式相同。模式以 ) 字符结束。下表列出了有效模式的示例。
| 模式 | 描述 | 
|---|---|
| a) | 如果 word 等于“a”,则匹配。 | 
| [[:alpha:]]) | 如果 word 是单个字母字符,则匹配。 | 
| ???) | 如果 word 长度恰好为三个字符,则匹配。 | 
| *.txt) | 如果 word 以字符“.txt”结尾,则匹配。 | 
| *) | 匹配 word 的任何值。最好将此作为 case 命令中的最后一个模式,以捕获与前一个模式不匹配的 word 的任何值,即捕获任何可能的无效值。 | 
以下是一个工作模式的示例:
xxxxxxxxxxread -r -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也可以使用竖条字符作为分隔符组合多个模式。这创建了一个“或”条件模式。这对于处理大写和小写字符非常有用。这里有一个例子:
xxxxxxxxxx# 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 -r -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在这里,我们修改了 case 菜单程序,使用字母而不是数字进行菜单选择。请注意,新模式如何允许输入大写和小写字母。
在4.0之前的bash版本中, case 只允许对成功匹配执行一个操作。匹配成功后,命令将终止。这里我们看到一个测试角色的脚本:
xxxxxxxxxx# case4-1: test a characterread -r -n 1 -p "Type a character > "echocase "$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." ;; esac运行此脚本会产生以下结果:
xxxxxxxxxx[me@linuxbox ~]$ case4-1Type a character > a'a' is lower case.该脚本在很大程度上有效,但如果一个字符与多个POSIX字符类匹配,则会失败。例如,字符“a”既是小写字母,也是字母,也是十六进制数字。在bash 4.0之前的版本中, case 无法匹配多个测试。现代版本的bash添加了 ;;& 符号来终止每个操作,所以现在我们可以这样做:
xxxxxxxxxx# case4-2: test a characterread -r -n 1 -p "Type a character > "echocase "$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." ;;& esac当我们运行此脚本时,我们得到以下结果:
xxxxxxxxxx[me@linuxbox ~]$ case4-2Type a character > a'a' is lower case.'a' is alphabetic.'a' is a visible character.'a' is a hexadecimal digit.添加 ;;& 语法允许 case 继续到下一个测试,而不是简单地终止。
case 命令是我们编程技巧包中的一个方便的补充。正如我们将在下一章中看到的,它是处理某些类型问题的完美工具。