高级Bash脚本编程指南(17):测试和分支(case与select结构)

来源:互联网 发布:卡拉ok播放器mac 编辑:程序博客网 时间:2024/06/06 09:41

高级Bash脚本编程指南(17):测试和分支(case与select结构)

成于坚持,败于止步

测试和分支(case与select结构)

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

case (in) / esac

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

case "$variable" in "$condition1" ) command... ;; "$condition2" ) command... ;;esac
注意四点:

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

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

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

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

看一个实例,看看case是如何使用的

#!/bin/bash# 测试字符串范围.while echo; echo "Hit a key, then hit return, q(Quit)"do read Keypressif [ "$Keypress" = "q" ]thenexit 0ficase "$Keypress" in  [[:lower:]]   ) echo "Lowercase letter";;  [[:upper:]]   ) echo "Uppercase letter";;  [0-9]         ) echo "Digit";;  *             ) echo "Punctuation, whitespace, or other";;esac      #  允许字符串的范围出现在[中括号]中,      #+ 或者出现在POSIX风格的[[双中括号中.done#  在这个例子的第一个版本中,#+ 测试大写和小写字符串的工作使用的是#+ [a-z] 和 [A-Z].#  这种用法在某些特定场合的或某些Linux发行版中不能够正常工作.#  POSIX 的风格更具可移植性.#  感谢Frank Wang指出了这点.exit 0
结果:
root@ubuntu:~/resource/shell-study/0613-2013# ./test4.sh Hit a key, then hit return, q(Quit)wLowercase letterHit a key, then hit return, q(Quit)1DigitHit a key, then hit return, q(Quit)EUppercase letterHit a key, then hit return, q(Quit)qroot@ubuntu:~/resource/shell-study/0613-2013#

接着看一个例子,使用case来创建菜单:

#!/bin/bashclear # 清屏.echo "          Contact List"echo "          ------- ----"echo "Choose one of the following persons:"echoecho "[E]vans, Roland"echo "[J]ones, Mildred"echo "[w]yu, Wang"echoread personfor para in $persondocase "$para" in"E" | "e" )# 接受大写或者小写输入.echoecho "Roland Evans"echo "4321 Floppy Dr."echo "Hardscrabble, CO 80753"echo "(303) 734-9874"echo "(303) 734-9892 fax"echo "revans@zzy.net"echo "Business partner & old friend";;# 注意, 每个选项后边都要以双分号;;结尾."J" | "j" )echoecho "Mildred Jones"echo "249 E. 7th St., Apt. 19"echo "New York, NY 10009"echo "(212) 533-2814"echo "(212) 533-9972 fax"echo "milliej@loisaida.com"echo "Ex-girlfriend"echo "Birthday: Feb. 11";;"W" | "w" )echoecho "yu wang"echo "1987-07-30"echo "New York, NY 10009"echo "(212) 533-2814"echo "(212) 533-9972 fax"echo "milliej@loisaida.com"echo "Ex-girlfriend";;  * )   # 默认选项.   # 空输入(敲回车RETURN), 也适用于这里.  echo  echo "Not yet in database."  ;;esacdoneechoexit 0
结果:
          Contact List          ------- ----Choose one of the following persons:[E]vans, Roland[J]ones, Mildred[w]yu, Wangj wMildred Jones249 E. 7th St., Apt. 19New York, NY 10009(212) 533-2814(212) 533-9972 faxmilliej@loisaida.comEx-girlfriendBirthday: Feb. 11yu wang1987-07-30New York, NY 10009(212) 533-2814(212) 533-9972 faxmilliej@loisaida.comEx-girlfriendroot@ubuntu:~/resource/shell-study/0613-2013#
上面两个实例同时使用了while和for循环的方式对书中代码进行了少许修改,书中给了个很聪明的实例,那就一定要看看了,O(∩_∩)O~

#! /bin/bashcase "$1" in"")# 没有命令行参数,或者第一个参数为空.echo "Usage: ${0##*/} <filename>"exit $E_PARAM;;  # 注意: ${0##*/} 是 ${var##pattern} 的一种替换形式. 得到的结果为$0.-*)FILENAME=./$1   #  如果传递进来的文件名参数($1)以一个破折号开头,#+ 那么用./$1来代替.#+ 这样后边的命令将不会把它作为一个选项来解释.echo $FILENAME;;* )FILENAME=$1     # 否则, $1.echo $FILENAME;;esac
结果:
root@ubuntu:~/resource/shell-study/0613-2013# ./test6.sh test1.sh test1.shroot@ubuntu:~/resource/shell-study/0613-2013# ./test6.sh -ehello ./-ehelloroot@ubuntu:~/resource/shell-study/0613-2013# 
不过呢,可能你并不理解,这个脚本到底聪明在哪里,要理解这个程序,你首先看一下下面的过程:

root@ubuntu:~/resource/shell-study/0613-2013# touch -ehellotouch: invalid option -- 'e'Try `touch --help' for more information.root@ubuntu:~/resource/shell-study/0613-2013# lsnested-loop.sh  test1.sh  test2.sh  test3.sh  test4.sh  test5.sh  test6.shroot@ubuntu:~/resource/shell-study/0613-2013# touch ./-ehelloroot@ubuntu:~/resource/shell-study/0613-2013# ls-ehello  nested-loop.sh  test1.sh  test2.sh  test3.sh  test4.sh  test5.sh  test6.sh
可以看到,当我使用touch -ehello去新建一个名叫“-ehello”的空文件时,提示错误,因为默认把-后面的字符解析为命令,要想避免shell把-解析为命令,就需要使用下面的第二种方式,touch ./-ehello,所以现在可以看出这个脚本的聪明之处了吧,放在把以“-”开头的文件名解析为命令

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

#! /bin/bashwhile [ $# -gt 0 ]; do    # 直到你用完所有的参数 . . .  case "$1" in    -d|--debug)# 是 "-d" 或 "--debug" 参数?DEBUG=1echo "open the debug mode";;    -c|--conf)CONFFILE="$2"shiftif [ ! -f $CONFFILE ]; thenecho "Error: Supplied file doesn't exist!"exit $E_CONFFILE     # 错误: 文件未发现.elseecho "command: -c $CONFFILE"fi;;  esac  shift       # 检查剩余的参数.done
结果:
root@ubuntu:~/resource/shell-study/0613-2013# ./test7.sh -dopen the debug moderoot@ubuntu:~/resource/shell-study/0613-2013# ./test7.sh -c test1.sh command: -c test1.shroot@ubuntu:~/resource/shell-study/0613-2013# 
使用命令替换来产生case变量
#!/bin/bash# case-cmd.sh: 使用命令替换来产生"case"变量.case $( arch ) in   # "arch" 返回机器体系的类型.                    # 等价于 'uname -m' ...i386 ) echo "80386-based machine";;i486 ) echo "80486-based machine";;i586 ) echo "Pentium-based machine";;i686 ) echo "Pentium2+-based machine";;*    ) echo "Other type of machine";;esacexit 0
结果:
root@ubuntu:~/resource/shell-study/0613-2013# chmod +x test8.sh root@ubuntu:~/resource/shell-study/0613-2013# ./test8.sh Pentium2+-based machineroot@ubuntu:~/resource/shell-study/0613-2013# 
简单的字符串匹配
#!/bin/bashmatch_string (){  MATCH=0  NOMATCH=90  PARAMS=2     # 此函数需要2个参数.  BAD_PARAMS=91  [ $# -eq $PARAMS ] || return $BAD_PARAMS  case "$1" in  "$2") return $MATCH;;  *   ) return $NOMATCH;;  esac}a=oneb=twoc=threed=twomatch_string $a     # 参数个数错误.echo $?             # 91match_string $a $b  # 不匹配echo $?             # 90match_string $b $d  # 匹配echo $?             # 0exit 0
结果:
root@ubuntu:~/resource/shell-study/0613-2013# chmod +x test9.sh root@ubuntu:~/resource/shell-study/0613-2013# ./test9.sh 91900root@ubuntu:~/resource/shell-study/0613-2013# 
检查输入字符是否为字母
#!/bin/bash# isalpha.sh: 使用"case"结构来过滤字符串.SUCCESS=0FAILURE=-1isalpha ()  # 检查输入的 *第一个字符* 是不是字母表上的字符.{if [ -z "$1" ]                # 没有参数传进来?then  return $FAILUREficase "$1" in[a-zA-Z]*) return $SUCCESS;;  # 以一个字母开头?*        ) return $FAILURE;;esac}             # 同C语言的"isalpha ()"函数比较一下.isalpha2 ()   # 测试 *整个字符串* 是否都是字母表上的字符.{  [ $# -eq 1 ] || return $FAILURE  case $1 in  *[!a-zA-Z]*|"") return $FAILURE;;               *) return $SUCCESS;;  esac}isdigit ()    # 测试 *整个字符串* 是否都是数字.{             # 换句话说, 就是测试一下是否是整数变量.  [ $# -eq 1 ] || return $FAILURE  case $1 in  *[!0-9]*|"") return $FAILURE;;            *) return $SUCCESS;;  esac}check_var ()  # 测试isalpha().{if isalpha "$@"then  echo "\"$*\" begins with an alpha character."  if isalpha2 "$@"  then        # 不需要测试第一个字符是否是non-alpha.    echo "\"$*\" contains only alpha characters."  else    echo "\"$*\" contains at least one non-alpha character."  fielse  echo "\"$*\" begins with a non-alpha character."              # 如果没有参数传递进来, 也是"non-alpha".fiecho}digit_check ()  # 测试isdigit().{if isdigit "$@"then  echo "\"$*\" contains only digits [0 - 9]."else  echo "\"$*\" has at least one non-digit character."fiecho}a=23skidoob=H3lloc=-What?d=What?e=`echo $b`   # 命令替换.f=AbcDefg=27234h=27a34i=27.34check_var $acheck_var $bcheck_var $ccheck_var $dcheck_var $echeck_var $fcheck_var     # 没有参数传递进来, 将会发生什么?digit_check $gdigit_check $hdigit_check $iexit 0        # S.C改进了这个脚本.
结果:
root@ubuntu:~/resource/shell-study/0613-2013# chmod +x test10.sh root@ubuntu:~/resource/shell-study/0613-2013# ./test10.sh ./test10.sh: line 16: return: -1: invalid optionreturn: usage: return [n]"23skidoo" begins with a non-alpha character."H3llo" begins with an alpha character../test10.sh: line 26: return: -1: invalid optionreturn: usage: return [n]"H3llo" contains at least one non-alpha character../test10.sh: line 16: return: -1: invalid optionreturn: usage: return [n]"-What?" begins with a non-alpha character."What?" begins with an alpha character../test10.sh: line 26: return: -1: invalid optionreturn: usage: return [n]"What?" contains at least one non-alpha character."H3llo" begins with an alpha character../test10.sh: line 26: return: -1: invalid optionreturn: usage: return [n]"H3llo" contains at least one non-alpha character."AbcDef" begins with an alpha character."AbcDef" contains only alpha characters../test10.sh: line 11: return: -1: invalid optionreturn: usage: return [n]./test10.sh: line 16: return: -1: invalid optionreturn: usage: return [n]"" begins with a non-alpha character."27234" contains only digits [0 - 9]../test10.sh: line 36: return: -1: invalid optionreturn: usage: return [n]"27a34" has at least one non-digit character../test10.sh: line 36: return: -1: invalid optionreturn: usage: return [n]"27.34" has at least one non-digit character.root@ubuntu:~/resource/shell-study/0613-2013# 

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

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

#!/bin/bashPS3='Choose your favorite vegetable: ' # 设置提示符字串.echoselect vegetable in "beans" "carrots" "potatoes" "onions" "rutabagas"do  echo  echo "Your favorite veggie is $vegetable."  echo "Yuck!"  echo  break  # 如果这里没有 'break' 会发生什么?doneexit 0
结果:
root@ubuntu:~/resource/shell-study/0613-2013# chmod +x test11.sh root@ubuntu:~/resource/shell-study/0613-2013# ./test11.sh 1) beans2) carrots3) potatoes4) onions5) rutabagasChoose your favorite vegetable: 2Your favorite veggie is carrots.Yuck!root@ubuntu:~/resource/shell-study/0613-2013# 
如果注释掉其中的break,结果也许会出于你的意料吧:

#!/bin/bashPS3='Choose your favorite vegetable: ' # 设置提示符字串.echoselect vegetable in "beans" "carrots" "potatoes" "onions" "rutabagas"do  echo  echo "Your favorite veggie is $vegetable."  echo "Yuck!"  echo  #break  # 如果这里没有 'break' 会发生什么?doneexit 0
结果:

root@ubuntu:~/resource/shell-study/0613-2013# ./test11.sh 1) beans2) carrots3) potatoes4) onions5) rutabagasChoose your favorite vegetable: 1Your favorite veggie is beans.Yuck!Choose your favorite vegetable: 2Your favorite veggie is carrots.Yuck!Choose your favorite vegetable: 3Your favorite veggie is potatoes.Yuck!Choose your favorite vegetable: 4Your favorite veggie is onions.Yuck!Choose your favorite vegetable: 5Your favorite veggie is rutabagas.Yuck!Choose your favorite vegetable: ^Croot@ubuntu:~/resource/shell-study/0613-2013# 
直到你使用ctrl+c才会退出

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

与忽略in list的for variable [in list]结构比较一下.

#!/bin/bashPS3='Choose your favorite vegetable: 'echochoice_of(){select vegetable# [in list]被忽略, 所以'select'使用传递给函数的参数.do  echo  echo "Your favorite veggie is $vegetable."  echo "Yuck!"  echo  breakdone}choice_of beans rice carrots radishes tomatoes spinach#         $1    $2   $3      $4       $5       $6#         传递给choice_of()的参数exit 0
结果:
root@ubuntu:~/resource/shell-study/0613-2013# chmod +x test112.sh root@ubuntu:~/resource/shell-study/0613-2013# ./test112.sh 1) beans2) rice3) carrots4) radishes5) tomatoes6) spinachChoose your favorite vegetable: 2Your favorite veggie is rice.Yuck!root@ubuntu:~/resource/shell-study/0613-2013# 
分支用法就说到这里

先到这里了,O(∩_∩)O~

我的专栏地址:http://blog.csdn.net/column/details/shell-daily-study.html

待续。。。。。

原创粉丝点击