總第5篇
之前,我們談到了if。這次我們來談另一種選擇結構——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代碼:
~~~
#include<stdio.h>
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;
}
}
~~~
也就是說,在C中,case后面如果不加break,那么在執行完匹配的語句后,將接著執行,而不管下面的條件匹配不匹配,直到遇到break為止。這樣的語法,十分靈活,那么bash中又如何實現呢?難道在末尾加一個分號?錯錯錯。答案是一個分號加一個扭號。
~~~
#!/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風格的。
見下表
<table border="1" cellpadding="0" cellspacing="0" width="495"><tbody><tr><td><p>類</p></td><td><p>描? 述</p></td><td><p>擴? 展</p></td></tr><tr><td><p><tt>[:alnum:]</tt></p></td><td><p>字母和數字字符</p></td><td><p><tt>[0-9a-zA-Z]</tt></p></td></tr><tr><td><p><tt>[:alpha:]</tt></p></td><td><p>?(letters)字母字符(字母)</p></td><td><p><tt>[a-zA-Z]</tt></p></td></tr><tr><td><p><tt>[:ascii:]</tt></p></td><td><p>7位ASCII</p></td><td><p><tt>[\x01-\x7F]</tt></p></td></tr><tr><td><p><tt>[:blank:]</tt></p></td><td><p>水平空白符(空格、制表符)</p></td><td><p><tt>[ \t]</tt></p></td></tr><tr><td><p><tt>[:cntrl:]</tt></p></td><td><p>控制字符</p></td><td><p><tt>[\x01-\x1F]</tt></p></td></tr><tr><td><p><tt>[:digit:]</tt></p></td><td><p>數字</p></td><td><p><tt>[0-9]</tt></p></td></tr><tr><td><p><tt>[:graph:]</tt></p></td><td><p>用墨水打印的字符(非空格、非控制字符)</p></td><td><p><tt>[^\x01-\x20]</tt></p></td></tr><tr><td><p><tt>[:lower:]</tt></p></td><td><p>小寫字母</p></td><td><p><tt>[a-z]</tt></p></td></tr><tr><td><p><tt>[:print:]</tt></p></td><td><p>可打印字符(圖形類加空格和制表符)</p></td><td><p><tt>[\t\x20-\xFF]</tt></p></td></tr><tr><td><p><tt>[:punct:]</tt></p></td><td><p>任意標點符號,如句點(.)和分號(;)</p></td><td><p><tt>[-!"#$%&'( )*+,./:;<=>?@[\\\]^_'{|}~]</tt></p></td></tr><tr><td><p><tt>[:space:]</tt></p></td><td><p>空白(換行、回車、制表符、空格、垂直制表符)</p></td><td><p><tt>[\n\r\t \x0B]</tt></p></td></tr><tr><td><p><tt>[:upper:]</tt></p></td><td><p>大寫字母</p></td><td><p><tt>[A-Z]</tt></p></td></tr><tr><td><p><tt>[:xdigit:]</tt></p></td><td><p>十六進制數字</p></td><td><p><tt>[0-9a-fA-F]</tt></p></td></tr></tbody></table>
這個表是從網友那里復制的,原文[《POSIX風格正則表達式](http://blog.csdn.net/joeblackzqq/article/details/7216046)》
實際使用的時候,還要在這些字符類的方括號外面,再套一層方括號才行。
~~~
#!/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鍵。或者鍵入**^[**字符,也是一樣的。
本系列(玩轉Bash腳本)更多文章,請訪問:[http://blog.csdn.net/column/details/wanbash.html](http://blog.csdn.net/column/details/wanbash.html)[](http://blog.csdn.net/column/details/wanbash.html)