轉自:http://hlee.iteye.com/blog/577628
case和select結構在技術上說並不是循環, 因爲它們並不對可執行代碼塊進行迭代. 但是和循環相似的是, 它們也依靠在代碼塊頂部或底部的條件判斷來決定程序的分支.
在代碼塊中控制程序分支
case (in) / esac
在shell中的case結構與C/C++中的switch結構是相同的. 它允許通過判斷來選擇代碼塊中多條路徑中的一條. 它的作用和多個if/then/else語句的作用相同, 是它們的簡化結構, 特別適用於創建菜單.
- case "$variable" in
- "$condition1" )
- command...
- ;;
- "$condition2" )
- command...
- ;;
- esac
- Note
*
對變量使用""並不是強制的, 因爲不會發生單詞分割.
*
每句測試行, 都以右小括號)來結尾.
*
每個條件判斷語句塊都以一對分號結尾 ;;.
*
case塊以esac (case的反向拼寫)結尾.
例子 10-24. 使用case
- #!/bin/bash
- # 測試字符串範圍.
- echo; echo "Hit a key, then hit return."
- read Keypress
- case "$Keypress" in
- [[:lower :]] ) echo "Lowercase letter" ;;
- [[:upper :]] ) echo "Uppercase letter" ;;
- [0-9] ) echo "Digit" ;;
- * ) echo "Punctuation, whitespace, or other" ;;
- esac # 允許字符串的範圍出現在[中括號]中,
- #+ 或者出現在POSIX風格的[[雙中括號中.
- # 在這個例子的第一個版本中,
- #+ 測試大寫和小寫字符串的工作使用的是
- #+ [a-z] 和 [A-Z].
- # 這種用法在某些特定場合的或某些Linux發行版中不能夠正常工作.
- # POSIX 的風格更具可移植性.
- # 感謝Frank Wang指出了這點.
- # 練習:
- # -----
- # 就像這個腳本所表現出來的, 它只允許單次的按鍵, 然後就結束了.
- # 修改這個腳本, 讓它能夠接受重複輸入,
- #+ 報告每次按鍵, 並且只有在"X"被鍵入時才結束.
- # 暗示: 將這些代碼都用"while"循環圈起來.
- exit 0
例子 10-25. 使用case來創建菜單
- #!/bin/bash
- # 未經處理的地址資料
- clear # 清屏.
- echo " Contact List"
- echo " ------- ----"
- echo "Choose one of the following persons:"
- echo
- echo "[E]vans, Roland"
- echo "[J]ones, Mildred"
- echo "[S]mith, Julie"
- echo "[Z]ane, Morris"
- echo
- read person
- case "$person" in
- # 注意, 變量是被""引用的.
- "E" | "e" )
- # 接受大寫或者小寫輸入.
- echo
- echo "Roland Evans"
- echo "4321 Floppy Dr."
- echo "Hardscrabble, CO 80753"
- echo "(303) 734-9874"
- echo "(303) 734-9892 fax"
- echo "[email protected]"
- echo "Business partner & old friend"
- ;;
- # 注意, 每個選項後邊都要以雙分號;;結尾.
- "J" | "j" )
- echo
- echo "Mildred Jones"
- echo "249 E. 7th St., Apt. 19"
- echo "New York, NY 10009"
- echo "(212) 533-2814"
- echo "(212) 533-9972 fax"
- echo "[email protected]"
- echo "Ex-girlfriend"
- echo "Birthday: Feb. 11"
- ;;
- # 後邊的 Smith 和 Zane 的信息在這裏就省略了.
- * )
- # 默認選項.
- # 空輸入(敲回車RETURN), 也適用於這裏.
- echo
- echo "Not yet in database."
- ;;
- esac
- echo
- # 練習:
- # -----
- # 修改這個腳本, 讓它能夠接受多個輸入,
- #+ 並且能夠顯示多個地址.
- exit 0
一個case的非常聰明的用法, 用來測試命令行參數.
- #! /bin/bash
- case "$1" in
- "" ) echo "Usage: ${0##*/} <filename>" ; exit $E_PARAM ;; # 沒有命令行參數,
- # 或者第一個參數爲空.
- # 注意: ${0##*/} 是 ${var##pattern} 的一種替換形式. 得到的結果爲$0.
- -*) FILENAME=./$1 ;; # 如果傳遞進來的文件名參數($1)以一個破折號開頭,
- #+ 那麼用./$1來代替.
- #+ 這樣後邊的命令將不會把它作爲一個選項來解釋.
- * ) FILENAME=$1 ;; # 否則, $1.
- esac
這是一個命令行參數處理的更容易理解的例子:
- #! /bin/bash
- while [ $# -gt 0 ]; do # 直到你用完所有的參數 . . .
- case "$1" in
- -d|--debug)
- # 是 "-d" 或 "--debug" 參數?
- DEBUG=1
- ;;
- -c|--conf)
- CONFFILE="$2"
- shift
- if [ ! -f $CONFFILE ]; then
- echo "Error: Supplied file doesn't exist!"
- exit $E_CONFFILE # 錯誤: 文件未發現.
- fi
- ;;
- esac
- shift # 檢查剩餘的參數.
- done
- # 來自Stefano Falsetto的 "Log2Rot" 腳本,
- #+ 並且是他的"rottlog"包的一部分.
- # 已得到使用許可.
例子 10-26. 使用命令替換來產生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" ;;
- esac
- exit 0
case結構也可以過濾通配(globbing)模式的字符串.
例子 10-27. 簡單的字符串匹配
- #!/bin/bash
- # match-string.sh: 簡單的字符串匹配
- match_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=one
- b=two
- c=three
- d=two
- match_string $a # 參數個數錯誤.
- echo $? # 91
- match_string $a $b # 不匹配
- echo $? # 90
- match_string $b $d # 匹配
- echo $? # 0
- exit 0
例子 10-28. 檢查輸入字符是否爲字母
- #!/bin/bash
- # isalpha.sh: 使用"case"結構來過濾字符串.
- SUCCESS=0
- FAILURE=-1
- isalpha () # 檢查輸入的 *第一個字符* 是不是字母表上的字符.
- {
- if [ -z "$1" ] # 沒有參數傳進來?
- then
- return $FAILURE
- fi
- case "$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."
- fi
- else
- echo "/"$*/" begins with a non-alpha character."
- # 如果沒有參數傳遞進來, 也是"non-alpha".
- fi
- echo
- }
- digit_check () # 測試isdigit().
- {
- if isdigit "$@"
- then
- echo "/"$*/" contains only digits [0 - 9]."
- else
- echo "/"$*/" has at least one non-digit character."
- fi
- echo
- }
- a=23skidoo
- b=H3llo
- c=-What?
- d=What?
- e=`echo $b ` # 命令替換.
- f=AbcDef
- g=27234
- h=27a34
- i=27.34
- check_var $a
- check_var $b
- check_var $c
- check_var $d
- check_var $e
- check_var $f
- check_var # 沒有參數傳遞進來, 將會發生什麼?
- #
- digit_check $g
- digit_check $h
- digit_check $i
- exit 0 # S.C改進了這個腳本.
# 練習:
# -----
# 編寫一個'isfloat ()'函數來測試浮點數.
# 暗示: 這個函數基本上與'isdigit ()'相同,
#+ 但是要添加一些小數點部分的處理.
select
select結構是建立菜單的另一種工具, 這種結構是從ksh中引入的.
select variable [in list]
do
command...
燽reak
done
提示用戶輸入選擇的內容(比如放在變量列表中). 注意: select命令使用PS3提示符, 默認爲(#?), 當然, 這可以修改.
例子 10-29. 使用select來創建菜單
- #!/bin/bash
- PS3='Choose your favorite vegetable: ' # 設置提示符字串.
- echo
- select vegetable in "beans" "carrots" "potatoes" "onions" "rutabagas"
- do
- echo
- echo "Your favorite veggie is $vegetable."
- echo "Yuck!"
- echo
- break # 如果這裏沒有 'break' 會發生什麼?
- done
- exit 0
如果忽略了in list列表, 那麼select命令將會使用傳遞到腳本的命令行參數($@), 或者是函數參數(當select是在函數中時).
與忽略in list的
for variable [in list]
結構比較一下.
例子 10-30. 使用函數中的select結構來創建菜單
- #!/bin/bash
- PS3='Choose your favorite vegetable: '
- echo
- choice_of()
- {
- select vegetable
- # [in list]被忽略, 所以'select'使用傳遞給函數的參數.
- do
- echo
- echo "Your favorite veggie is $vegetable."
- echo "Yuck!"
- echo
- break
- done
- }
- choice_of beans rice carrots radishes tomatoes spinach
- # $1 $2 $3 $4 $5 $6
- # 傳遞給choice_of()的參數
- exit 0