1. 程式人生 > 實用技巧 >Bash:選擇結構之case

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程式碼:

  1. #include<stdio.h>
  2. int main(){
  3. char ans;
  4. printf("你喜歡程式設計麼:");
  5. scanf("%c",&ans);
  6. switch(ans){
  7. case 'y':
  8. case 'Y':printf("我也是\n");break;
  9. case 'n':
  10. case 'N':printf("sorry,跟你沒什麼好談的\n");break;
  11. }
  12. }
也就是說,在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風格的。

見下表

描 述

擴 展

[: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]

這個表是從網友那裡複製的,原文《POSIX風格正則表示式

實際使用的時候,還要在這些字元類的方括號外面,再套一層方括號才行。

#!/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鍵。或者鍵入^[字元,也是一樣的。