流程控制:case 分支

    Bash 的多选复合命令称为 case。它的语法规则如下所示:

    如果我们看一下第28章中的读菜单程序,我们就知道了用来应对一个用户选项的逻辑流程:

    1. # read-menu: a menu driven system information program
    2. clear
    3. echo "
    4. Please Select:
    5. 1. Display System Information
    6. 2. Display Disk Space
    7. 3. Display Home Space Utilization
    8. 0. Quit
    9. "
    10. read -p "Enter selection [0-3] > "
    11. if [[ $REPLY =~ ^[0-3]$ ]]; then
    12. if [[ $REPLY == 0 ]]; then
    13. echo "Program terminated."
    14. exit
    15. fi
    16. if [[ $REPLY == 1 ]]; then
    17. echo "Hostname: $HOSTNAME"
    18. uptime
    19. exit
    20. fi
    21. if [[ $REPLY == 2 ]]; then
    22. df -h
    23. exit
    24. fi
    25. if [[ $REPLY == 3 ]]; then
    26. if [[ $(id -u) -eq 0 ]]; then
    27. echo "Home Space Utilization (All Users)"
    28. du -sh /home/*
    29. else
    30. echo "Home Space Utilization ($USER)"
    31. du -sh $HOME
    32. fi
    33. exit
    34. fi
    35. else
    36. echo "Invalid entry." >&2
    37. exit 1
    38. fi

    使用 case 语句,我们可以用更简单的代码替换这种逻辑:

    1. #!/bin/bash
    2. # case-menu: a menu driven system information program
    3. clear
    4. Please Select:
    5. 1. Display System Information
    6. 3. Display Home Space Utilization
    7. 0. Quit
    8. "
    9. read -p "Enter selection [0-3] > "
    10. case $REPLY in
    11. 0) echo "Program terminated."
    12. exit
    13. ;;
    14. 1) echo "Hostname: $HOSTNAME"
    15. uptime
    16. ;;
    17. 2) df -h
    18. ;;
    19. 3) if [[ $(id -u) -eq 0 ]]; then
    20. echo "Home Space Utilization (All Users)"
    21. du -sh /home/*
    22. else
    23. echo "Home Space Utilization ($USER)"
    24. du -sh $HOME
    25. fi
    26. ;;
    27. *) echo "Invalid entry" >&2
    28. exit 1
    29. ;;
    30. esac

    case 命令检查一个变量值,在我们这个例子中,就是 REPLY 变量的变量值,然后试图去匹配其中一个具体的模式。当与之相匹配的模式找到之后,就会执行与该模式相关联的命令。若找到一个模式之后,就不会再继续寻找。

    32.1.1 模式

    这里 case 语句使用的模式和路径展开中使用的那些是一样的。模式以一个 “)” 为终止符。这里是一些有效的模式。

    还可以使用竖线字符作为分隔符,把多个模式结合起来。这就创建了一个 “或” 条件模式。这对于处理诸如大小写字符很有用处。例如:

    1. #!/bin/bash
    2. # case-menu: a menu driven system information program
    3. clear
    4. echo "
    5. Please Select:
    6. A. Display System Information
    7. B. Display Disk Space
    8. C. Display Home Space Utilization
    9. Q. Quit
    10. "
    11. read -p "Enter selection [A, B, C or Q] > "
    12. case $REPLY in
    13. q|Q) echo "Program terminated."
    14. exit
    15. ;;
    16. a|A) echo "Hostname: $HOSTNAME"
    17. uptime
    18. ;;
    19. b|B) df -h
    20. c|C) if [[ $(id -u) -eq 0 ]]; then
    21. echo "Home Space Utilization (All Users)"
    22. du -sh /home/*
    23. else
    24. echo "Home Space Utilization ($USER)"
    25. du -sh $HOME
    26. fi
    27. ;;
    28. *) echo "Invalid entry" >&2
    29. exit 1
    30. ;;
    31. esac

    这里,我们更改了 case-menu 程序的代码,用字母来代替数字做为菜单选项。注意新模式如何使得大小写字母都是有效的输入选项。

    32.1.2 多个模式的组合

    早于版本号4.0的 bash,case 语法只允许执行与一个成功匹配的模式相关联的动作。匹配成功之后,命令将会终止。这里我们看一个测试一个字符的脚本:

    1. #!/bin/bash
    2. # case4-1: test a character
    3. read -n 1 -p "Type a character > "
    4. echo
    5. case $REPLY in
    6. [[:upper:]]) echo "'$REPLY' is upper case." ;;
    7. [[:lower:]]) echo "'$REPLY' is lower case." ;;
    8. [[:alpha:]]) echo "'$REPLY' is alphabetic." ;;
    9. [[:digit:]]) echo "'$REPLY' is a digit." ;;
    10. [[:graph:]]) echo "'$REPLY' is a visible character." ;;
    11. [[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;
    12. [[:space:]]) echo "'$REPLY' is a whitespace character." ;;
    13. [[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;
    14. esac

    运行这个脚本,输出这些内容:

    大多数情况下这个脚本工作是正常的,但若输入的字符不止与一个 POSIX 字符集匹配的话,这时脚本就会出错。例如,字符 “a” 既是小写字母,也是一个十六进制的数字。早于4.0的 bash,对于 case 语法绝不能匹配多个测试条件。现在的 bash 版本,添加 “;;&” 表达式来终止每个行动,所以现在我们可以做到这一点:

    1. #!/bin/bash
    2. # case4-2: test a character
    3. read -n 1 -p "Type a character > "
    4. echo
    5. case $REPLY in
    6. [[:upper:]]) echo "'$REPLY' is upper case." ;;&
    7. [[:lower:]]) echo "'$REPLY' is lower case." ;;&
    8. [[:alpha:]]) echo "'$REPLY' is alphabetic." ;;&
    9. [[:digit:]]) echo "'$REPLY' is a digit." ;;&
    10. [[:graph:]]) echo "'$REPLY' is a visible character." ;;&
    11. [[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;&
    12. [[:space:]]) echo "'$REPLY' is a whitespace character." ;;&
    13. [[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;&
    14. esac
    1. [me@linuxbox ~]$ case4-2
    2. Type a character > a
    3. 'a' is lower case.
    4. 'a' is alphabetic.
    5. 'a' is a hexadecimal digit.

    添加的 “;;&” 的语法允许 case 语句继续执行下一条测试,而不是简单地终止运行。

    case 命令是我们编程技巧口袋中的一个便捷工具。在下一章中我们将看到,对于处理某些类型的问题来说,case 命令是一个完美的工具。

    • Bash 参考手册的条件构造一节详尽的介绍了 case 命令: