Bash:選擇結構之case
case與if
if用於選擇的條件,不是很多的情況,如果選擇的條件太多,一系列的if,elif,,也是醉了。沒錯,case在bash中的地位,和switch在C中的地位一樣。但是用法可大有不同。
程式碼塊
任何語言都有程式碼塊的概念,C語言中的花括號{ },python中的冒號和縮排。bash中的程式碼塊風格不是很統一。但是在選擇結構中是相同的——反語。if結構使用if...fi標識一個程式碼塊的作用域,而case也是用case...esac表示塊作用域的。。
基本結構
看例子echo.sh:
#!/bin/bash
str="is good "
read -p "你喜歡什麼顏色:" color
case $color in
red) echo -e "\e[1;31m$color $str \e[0m";;
green) echo -e "\e[1;32m$color $str \e[0m";;
yellow) echo -e "\e[1;33m$color $str \e[0m";;
blue) echo -e "\e[1;34m$color $str \e[0m";;
*) echo -e "\e[1;30m這是什麼顏色?\e[0m";;
esac
這段程式碼,就是根據你的輸入來顯示不同顏色的文字。關於echo語句的寫法,這裡不細說,每句分別設定了不同的文字顏色。重點去看一下這個結構。
把變數color的值,依次和下面右括號裡的值做比較,如果相同,就執行後面的語句。
來執行一下
兩個分號;;
注意每個條件末尾都是兩個分號。這很好理解,就像C語言的switch裡面每個case都經常會有一個break一樣。因為每個條件滿足的時候,需要執行的語句可能不止一句。而在bash中多個語句在一行是用一個分號間隔的。這樣兩個分號就表示著語句結束,另外加一個空語句,也就是說要執行的語句都完成了。
與c語言的switch不同的是,c語言中每個條件(case語句)後面不一定都要有break,如果沒有break,則繼續執行下面的條件的語句。如果你想像c語言那樣多個條件共用一套語句,怎麼辦呢?請繼續閱讀。
分號扭號;&
所謂扭號,就是And符號——&,我覺得叫扭號更簡潔。。看一段C程式碼:
- int main(){
- char ans;
- printf("你喜歡程式設計麼:");
- scanf("%c",&ans);
- switch(ans){
- case 'y':
- case 'Y':printf("我也是\n");break;
-
case 'n':
- case 'N':printf("sorry,跟你沒什麼好談的\n");break;
- }
- }
#!/bin/bash
read -p "你喜歡程式設計麼:(y/n):" ans
case $ans in
y);&
Y) echo "我也是";;
n);&
N) echo "sorry,跟你沒什麼好談的";;
esac
分號分號扭號;;&
除了 ;&結束語句以外,還有一種是 ;;& 來結束語句的用法。但是意義有所不同,;;&的用法是使得條件越來越精確。
#!/bin/bash
read -p "請輸入一個區號:" num
case $num in
*)echo -n "中國";;&
03*)echo -n "河北省";;&
??10)echo "邯鄲市";;
??11)echo "石家莊";;
??17)echo "滄州市";;
07*)echo -n "江西省";;&
??91)echo "南昌市";;
??92)echo "九江市";;
??97)echo "贛州市";;
esac
關於萬用字元*和?,我們下面會講到。這裡,你只需要知道,條件語句以 ;;& 結束的時候,程式執行到這裡不會停止,會繼續測試下面條件,如果滿足繼續執行,直到遇到 ;; 或esac
注意,以上這兩種扭號的用法,是bash自己的 特性,對於其他shell,並不支援。如果考慮移植性,就不要這樣寫了。僅僅用正規的兩個分號就可以了。
右括號中的模式
基本正則
)右括號,類似c語言中switch中的case。然而與之不同的是,Bash中的右括號裡面,不僅支援完整的字串(c語言中只支援整型資料,包括int和char
,不支援字串)還支援“模式”匹配。模式的概念,如果瞭解正則表示式,那麼就很容易理解了。不過case語句中與正則表示式略有不同。貌似支援的並不完整。
- *是匹配0個或多個任何字元。
- ?是匹配一個字元。可以看做一個佔位符。
- [ ]表示一個範圍。
- ( )列舉字串。但是需要轉義
#!/bin/bash
read -p "請輸入一個數字:" num
case $num in
2*)echo "匹配2*";;&
2?)echo "匹配2?";;&
[0-9])echo "匹配[0-9]";;
esac
方括號表示範圍,你可以使用[123]來匹配1或2或3,而不是123。也可以使用[0-9]、[a-zA-Z]這樣的寫法,熟悉正則的同學,這些都不是問題。注意我上面用到了 ;;& 也就是說在匹配了一個模式之後,不會停止,還會繼續向下執行。執行效果:
列舉字串
一般的寫法比如(123|456|789)匹配123,456,789這三個數。但是因為case中右括號有特殊含義,所以進行了轉義。
然後我的寫法就是(123|456|789\),但是這樣有個問題就是789無法得到匹配,123和456倒是可以,我也是不明所以(見笑,知情者望告知)。
於是我又改了一版,終於匹配完全——(123|456|789|\)。
現在加上一句到剛才的指令碼中——(123|456|789 \))echo "匹配(123|456|789|\)";;&
POSIX字元類
一般的程式語言中的正則表示式,支援\w,\d這類的字元類(Character Classes)。而Unix-like系統上,支援的字元類是POSIX風格的。
見下表
類 |
描 述 |
擴 展 |
[:alnum:] |
字母和數字字元 |
[0-9a-zA-Z] |
[:alpha:] |
(letters)字母字元(字母) |
[a-zA-Z] |
[:ascii:] |
7位ASCII |
[\x01-\x7F] |
[:blank:] |
水平空白符(空格、製表符) |
[ \t] |
[:cntrl:] |
控制字元 |
[\x01-\x1F] |
[:digit:] |
數字 |
[0-9] |
[:graph:] |
用墨水列印的字元(非空格、非控制字元) |
[^\x01-\x20] |
[:lower:] |
小寫字母 |
[a-z] |
[:print:] |
可列印字元(圖形類加空格和製表符) |
[\t\x20-\xFF] |
[:punct:] |
任意標點符號,如句點(.)和分號(;) |
[-!"#$%&'( )*+,./:;<=>?@[\\\]^_'{|}~] |
[:space:] |
空白(換行、回車、製表符、空格、垂直製表符) |
[\n\r\t \x0B] |
[:upper:] |
大寫字母 |
[A-Z] |
[:xdigit:] |
十六進位制數字 |
[0-9a-fA-F] |
實際使用的時候,還要在這些字元類的方括號外面,再套一層方括號才行。
#!/bin/bash
read -p "請任意輸入,可以包含空格等空白符" ch
#echo $ch
case $ch in
*[[:lower:]]*)echo lower;;&
*[[:upper:]]*)echo upper;;&
*[[:digit:]]*)echo digit;;&
*[[:punct:]]*)echo punct;;&
*[[:ascii:]]*)echo ascii;;&
*[[:cntrl:]]*)echo cntrl;;&
*[[:print:]]*)echo print;;&
*[[:space:]]*)echo space;;&
*[[:xdigit:]]*)echo xdigit;;&
*[[:graph:]]*)echo graph;;&
*[[:blank:]]*)echo blank;;&
*[[:alnum:]]*)echo alnum;;&
*[[:alpha:]]*)echo alpha;;&
esac
這個指令碼,大家自己去測試吧。當然了有些字元(比如控制字元)貌似不好輸入。這樣不好測試[:cntrl:]這個字元類,這裡我告訴您一下,ascii碼中27對應的控制字元就是ESC。測試指令碼的時候,可以按一下ESC鍵。或者鍵入^[字元,也是一樣的。