支(case与select结构)

case和select结构在技术上说并不是循环, 因为它们并不对可执行代码块进行迭代. 但是和循环相似的是, 它们也依靠在代码块顶部或底部的条件判断来决定程序的分支.

在代码块中控制程序分支

case (in) / esac

   在shell中的case结构与C/C++中的switch结构是相同的. 它允许通过判断来选择代码块中多条路径中的一条. 它的作用和多个if/then/else语句的作用相同, 是它们的简化结构, 特别适用于创建菜单.

Ruby代码  收藏代码
  1. case"$variable"in

  2. "$condition1" )  

  3. command...  

  4. ;;  

  5. "$condition2" )  

  6. command...  

  7. ;;  

  8. esac  

  9. Note    





       *

         对变量使用""并不是强制的, 因为不会发生单词分割.
       *

         每句测试行, 都以右小括号)来结尾.
       *

         每个条件判断语句块都以一对分号结尾 ;;.
       *

         case块以esac (case的反向拼写)结尾.

   例子 10-24. 使用case

Ruby代码  收藏代码
  1. #!/bin/bash

  2. # 测试字符串范围.

  3. echo; echo "Hit a key, then hit return."

  4. read Keypress  

  5. case"$Keypress"in

  6.  [[:lower:]]   ) echo "Lowercase letter";;  

  7.  [[:upper:]]   ) echo "Uppercase letter";;  

  8.  [0-9]         ) echo "Digit";;  

  9.  *             ) echo "Punctuation, whitespace, or other";;  

  10. esac      #  允许字符串的范围出现在[中括号]中,

  11. #+ 或者出现在POSIX风格的[[双中括号中.

  12. #  在这个例子的第一个版本中,

  13. #+ 测试大写和小写字符串的工作使用的是

  14. #+ [a-z] 和 [A-Z].

  15. #  这种用法在某些特定场合的或某些Linux发行版中不能够正常工作.

  16. #  POSIX 的风格更具可移植性.

  17. #  感谢Frank Wang指出了这点.

  18. #  练习:

  19. #  -----

  20. #  就像这个脚本所表现出来的, 它只允许单次的按键, 然后就结束了.

  21. #  修改这个脚本, 让它能够接受重复输入,

  22. #+ 报告每次按键, 并且只有在"X"被键入时才结束.

  23. #  暗示: 将这些代码都用"while"循环圈起来.

  24. exit 0  



   例子 10-25. 使用case来创建菜单

Ruby代码  收藏代码
  1. #!/bin/bash

  2. # 未经处理的地址资料

  3. clear # 清屏.

  4. echo "          Contact List"

  5. echo "          ------- ----"

  6. echo "Choose one of the following persons:"

  7. echo  

  8. echo "[E]vans, Roland"

  9. echo "[J]ones, Mildred"

  10. echo "[S]mith, Julie"

  11. echo "[Z]ane, Morris"

  12. echo  

  13. read person  

  14. case"$person"in

  15. # 注意, 变量是被""引用的.

  16. "E" | "e" )  

  17. # 接受大写或者小写输入.

  18.  echo  

  19.  echo "Roland Evans"

  20.  echo "4321 Floppy Dr."

  21.  echo "Hardscrabble, CO 80753"

  22.  echo "(303) 734-9874"

  23.  echo "(303) 734-9892 fax"

  24.  echo "revans@zzy.net"

  25.  echo "Business partner & old friend"

  26.  ;;  

  27. # 注意, 每个选项后边都要以双分号;;结尾.

  28. "J" | "j" )  

  29.  echo  

  30.  echo "Mildred Jones"

  31.  echo "249 E. 7th St., Apt. 19"

  32.  echo "New York, NY 10009"

  33.  echo "(212) 533-2814"

  34.  echo "(212) 533-9972 fax"

  35.  echo "milliej@loisaida.com"

  36.  echo "Ex-girlfriend"

  37.  echo "Birthday: Feb. 11"

  38.  ;;  

  39. # 后边的 Smith 和 Zane 的信息在这里就省略了.

  40.          * )  

  41. # 默认选项.

  42. # 空输入(敲回车RETURN), 也适用于这里.

  43.   echo  

  44.   echo "Not yet in database."

  45.  ;;  

  46. esac  

  47. echo  

  48. #  练习:

  49. #  -----

  50. #  修改这个脚本, 让它能够接受多个输入,

  51. #+ 并且能够显示多个地址.

  52. exit 0  



   一个case的非常聪明的用法, 用来测试命令行参数.

Ruby代码  收藏代码
  1. #! /bin/bash

  2. case"$1"in

  3. "") echo "Usage: ${0##*/} <filename>"; exit $E_PARAM;;  # 没有命令行参数,  

  4. # 或者第一个参数为空.

  5. # 注意: ${0##*/} 是 ${var##pattern} 的一种替换形式. 得到的结果为$0.

  6. -*) FILENAME=./$1;;   #  如果传递进来的文件名参数($1)以一个破折号开头,

  7. #+ 那么用./$1来代替.

  8. #+ 这样后边的命令将不会把它作为一个选项来解释.

  9. * ) FILENAME=$1;;     # 否则, $1.

  10. esac  



   这是一个命令行参数处理的更容易理解的例子:

Ruby代码  收藏代码
  1. #! /bin/bash

  2. while [ $# -gt 0 ]; do    # 直到你用完所有的参数 . . .

  3. case"$1"in

  4.    -d|--debug)  

  5. # 是 "-d" 或 "--debug" 参数?

  6.              DEBUG=1  

  7.              ;;  

  8.    -c|--conf)  

  9.              CONFFILE="$2"

  10.              shift  

  11. if [ ! -f $CONFFILE ]; then

  12.                echo "Error: Supplied file doesn't exist!"

  13.                exit $E_CONFFILE# 错误: 文件未发现.

  14.              fi  

  15.              ;;  

  16.  esac  

  17.  shift       # 检查剩余的参数.

  18. done  

  19. #  来自Stefano Falsetto的 "Log2Rot" 脚本,

  20. #+ 并且是他的"rottlog"包的一部分.

  21. #  已得到使用许可.



   例子 10-26. 使用命令替换来产生case变量

Ruby代码  收藏代码
  1. #!/bin/bash

  2. # case-cmd.sh: 使用命令替换来产生"case"变量.

  3. case $( arch ) in# "arch" 返回机器体系的类型.

  4. # 等价于 'uname -m' ...

  5. i386 ) echo "80386-based machine";;  

  6. i486 ) echo "80486-based machine";;  

  7. i586 ) echo "Pentium-based machine";;  

  8. i686 ) echo "Pentium2+-based machine";;  

  9. *    ) echo "Other type of machine";;  

  10. esac  

  11. exit 0  



   case结构也可以过滤通配(globbing)模式的字符串.

   例子 10-27. 简单的字符串匹配

Ruby代码  收藏代码
  1. #!/bin/bash

  2. # match-string.sh: 简单的字符串匹配

  3. match_string ()  

  4. {  

  5.  MATCH=0  

  6.  NOMATCH=90  

  7.  PARAMS=2     # 此函数需要2个参数.

  8.  BAD_PARAMS=91  

  9.  [ $# -eq $PARAMS ] || return $BAD_PARAMS

  10. case"$1"in

  11. "$2") return$MATCH;;  

  12.  *   ) return$NOMATCH;;  

  13.  esac  

  14. }    

  15. a=one  

  16. b=two  

  17. c=three  

  18. d=two  

  19. match_string $a# 参数个数错误.

  20. echo $?             # 91

  21. match_string $a$b# 不匹配

  22. echo $?             # 90

  23. match_string $b$d# 匹配

  24. echo $?             # 0

  25. exit 0    



   例子 10-28. 检查输入字符是否为字母

Ruby代码  收藏代码
  1. #!/bin/bash

  2. # isalpha.sh: 使用"case"结构来过滤字符串.

  3. SUCCESS=0  

  4. FAILURE=-1  

  5. isalpha ()  # 检查输入的 *第一个字符* 是不是字母表上的字符.

  6. {  

  7. if [ -z "$1" ]                # 没有参数传进来?

  8. then

  9. return$FAILURE

  10. fi  

  11. case"$1"in

  12. [a-zA-Z]*) return$SUCCESS;;  # 以一个字母开头?

  13. *        ) return$FAILURE;;  

  14. esac  

  15. }             # 同C语言的"isalpha ()"函数比较一下.

  16. isalpha2 ()   # 测试 *整个字符串* 是否都是字母表上的字符.

  17. {  

  18.  [ $# -eq 1 ] || return $FAILURE

  19. case$1in

  20.  *[!a-zA-Z]*|"") return$FAILURE;;  

  21.               *) return$SUCCESS;;  

  22.  esac  

  23. }  

  24. isdigit ()    # 测试 *整个字符串* 是否都是数字.

  25. {             # 换句话说, 就是测试一下是否是整数变量.

  26.  [ $# -eq 1 ] || return $FAILURE

  27. case$1in

  28.  *[!0-9]*|"") return$FAILURE;;  

  29.            *) return$SUCCESS;;  

  30.  esac  

  31. }  

  32. check_var ()  # 测试isalpha().

  33. {  

  34. if isalpha "$@"

  35. then

  36.  echo "\"$*\" begins with an alpha character."

  37. if isalpha2 "$@"

  38. then# 不需要测试第一个字符是否是non-alpha.

  39.    echo "\"$*\" contains only alpha characters."

  40. else

  41.    echo "\"$*\" contains at least one non-alpha character."

  42.  fi    

  43. else

  44.  echo "\"$*\" begins with a non-alpha character."

  45. # 如果没有参数传递进来, 也是"non-alpha".

  46. fi  

  47. echo  

  48. }  

  49. digit_check ()  # 测试isdigit().

  50. {  

  51. if isdigit "$@"

  52. then

  53.  echo "\"$*\" contains only digits [0 - 9]."

  54. else

  55.  echo "\"$*\" has at least one non-digit character."

  56. fi  

  57. echo  

  58. }  

  59. a=23skidoo  

  60. b=H3llo  

  61. c=-What?  

  62. d=What?  

  63. e=`echo $b`   # 命令替换.

  64. f=AbcDef  

  65. g=27234  

  66. h=27a34  

  67. i=27.34  

  68. check_var $a

  69. check_var $b

  70. check_var $c

  71. check_var $d

  72. check_var $e

  73. check_var $f

  74. check_var     # 没有参数传递进来, 将会发生什么?

  75. #

  76. digit_check $g

  77. digit_check $h

  78. digit_check $i

  79. exit 0        # S.C改进了这个脚本.



    # 练习:
    # -----
    #  编写一个'isfloat ()'函数来测试浮点数.
    #  暗示: 这个函数基本上与'isdigit ()'相同,
    #+ 但是要添加一些小数点部分的处理.

select

   select结构是建立菜单的另一种工具, 这种结构是从ksh中引入的.

   select variable [in list]
   do
    command...
   燽reak
   done

   提示用户输入选择的内容(比如放在变量列表中). 注意: select命令使用PS3提示符, 默认为(#?), 当然, 这可以修改.

   例子 10-29. 使用select来创建菜单

Ruby代码  收藏代码
  1. #!/bin/bash

  2. PS3='Choose your favorite vegetable: '# 设置提示符字串.

  3. echo  

  4. select vegetable in"beans""carrots""potatoes""onions""rutabagas"

  5. do

  6.  echo  

  7.  echo "Your favorite veggie is $vegetable."

  8.  echo "Yuck!"

  9.  echo  

  10. break# 如果这里没有 'break' 会发生什么?

  11. done  

  12. exit 0  



   如果忽略了in list列表, 那么select命令将会使用传递到脚本的命令行参数($@), 或者是函数参数(当select是在函数中时).

   与忽略in list的

   for variable [in list]
   结构比较一下.

   例子 10-30. 使用函数中的select结构来创建菜单
Ruby代码  收藏代码
  1. #!/bin/bash

  2. PS3='Choose your favorite vegetable: '

  3. echo  

  4. choice_of()  

  5. {  

  6. select vegetable  

  7. # [in list]被忽略, 所以'select'使用传递给函数的参数.

  8. do

  9.  echo  

  10.  echo "Your favorite veggie is $vegetable."

  11.  echo "Yuck!"

  12.  echo  

  13. break

  14. done  

  15. }  

  16. choice_of beans rice carrots radishes tomatoes spinach  

  17. #         $1    $2   $3      $4       $5       $6

  18. #         传递给choice_of()的参数

  19. exit 0