Free mind

          Be fresh and eager every morning, and tired and satisfied every night.
          posts - 39, comments - 2, trackbacks - 0, articles - 0
             :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          Linux 技巧: Bash 測(cè)試和比較函數(shù)

          Posted on 2007-05-19 10:14 morphis 閱讀(251) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): 2. System

          Linux 技巧: Bash 測(cè)試和比較函數(shù)

          test、[、[[、((、和 if-then-else 解密

          developerWorks

          級(jí)別: 中級(jí)

          Ian Shields (ishields@us.ibm.com), 高級(jí)程序員, IBM

          2007 年 3 月 16 日

          您是否為 Bash shell 中大量的測(cè)試和比較選項(xiàng)而困惑呢?這個(gè)技巧可以幫助您解密不同類(lèi)型的文件、算術(shù)和字符串測(cè)試,這樣您就能夠知道什么時(shí)候使用 test[ ][[ ]](( ))if-then-else 了。

          Bash shell 在當(dāng)今的許多 Linux® 和 UNIX® 系統(tǒng)上都可使用,是 Linux 上常見(jiàn)的默認(rèn) shell。Bash 包含強(qiáng)大的編程功能,其中包括豐富的可測(cè)試文件類(lèi)型和屬性的函數(shù),以及在多數(shù)編程語(yǔ)言中可以使用的算術(shù)和字符串比較函數(shù)。理解不同的測(cè)試并認(rèn)識(shí)到 shell 還能把一些操作符解釋成 shell 元字符,是成為高級(jí) shell 用戶(hù)的重要一步。這篇文章摘自 developerWorks 教程 LPI exam 102 prep: Shells, scripting, programming, and compiling,介紹了如何理解和使用 Bash shell 的測(cè)試和比較操作。

          這個(gè)技巧解釋了 shell 測(cè)試和比較函數(shù),演示了如何向 shell 添加編程功能。您可能已經(jīng)看到過(guò)使用 && 和 || 操作符的簡(jiǎn)單 shell 邏輯,它允許您根據(jù)前一條命令的退出狀態(tài)(正確退出或伴隨錯(cuò)誤退出)而執(zhí)行后一條命令。在這個(gè)技巧中,將看到如何把這些基本的技術(shù)擴(kuò)展成更復(fù)雜的 shell 編程。

          測(cè)試

          在任何一種編程語(yǔ)言中,學(xué)習(xí)了如何給變量分配值和傳遞參數(shù)之后,都需要測(cè)試這些值和參數(shù)。在 shell 中,測(cè)試會(huì)設(shè)置返回的狀態(tài),這與其他命令執(zhí)行的功能相同。實(shí)際上,test 是個(gè)內(nèi)置命令

          test 和 [

          內(nèi)置命令 test 根據(jù)表達(dá)式expr 求值的結(jié)果返回 0(真)或 1(假)。也可以使用方括號(hào):test expr 和 [ expr ] 是等價(jià)的。 可以用 $? 檢查返回值;可以使用 && 和 || 操作返回值;也可以用本技巧后面介紹的各種條件結(jié)構(gòu)測(cè)試返回值。


          清單 1. 一些簡(jiǎn)單測(cè)試
          [ian@pinguino ~]$ test 3 -gt 4 && echo True || echo false
                                  false
                                  [ian@pinguino ~]$ [ "abc" != "def" ];echo $?
                                  0
                                  [ian@pinguino ~]$ test -d "$HOME" ;echo $?
                                  0
                                  

          在清單 1 的第一個(gè)示例中,-gt 操作符對(duì)兩個(gè)字符值之間執(zhí)行算術(shù)比較。在第二個(gè)示例中,用 [ ] 的形式比較兩個(gè)字符串不相等。在最后一個(gè)示例中,測(cè)試 HOME 變量的值,用單目操作符 -d 檢查它是不是目錄。

          可以用 -eq、 -ne-lt、 -le、 -gt 或 -ge 比較算術(shù)值,它們分別表示等于、不等于、小于、小于等于、大于、大于等于。

          可以分別用操作符 =!=<> 比較字符串是否相等、不相等或者第一個(gè)字符串的排序在第二個(gè)字符串的前面或后面。單目操作符 -z 測(cè)試 null 字符串,如果字符串非空 -n 返回 True(或者根本沒(méi)有操作符)。

          說(shuō)明:shell 也用 <> 操作符進(jìn)行重定向,所以必須用 \<\> 加以轉(zhuǎn)義。清單 2 顯示了字符串測(cè)試的更多示例。檢查它們是否如您預(yù)期的一樣。


          清單 2. 一些字符串測(cè)試
          [ian@pinguino ~]$ test "abc" = "def" ;echo $?
                                  1
                                  [ian@pinguino ~]$ [ "abc" != "def" ];echo $?
                                  0
                                  [ian@pinguino ~]$ [ "abc" \< "def" ];echo $?
                                  0
                                  [ian@pinguino ~]$ [ "abc" \> "def" ];echo $?
                                  1
                                  [ian@pinguino ~]$ [ "abc" \<"abc" ];echo $?
                                  1
                                  [ian@pinguino ~]$ [ "abc" \> "abc" ];echo $?
                                  1
                                  

          表 1 顯示了一些更常見(jiàn)的文件測(cè)試。如果被測(cè)試的文件存在,而且有指定的特征,則結(jié)果為 True。

          表 1. 一些常見(jiàn)的文件測(cè)試
          操作符 特征
          -d 目錄
          -e 存在(也可以用 -a)
          -f 普通文件
          -h 符號(hào)連接(也可以用 -L)
          -p 命名管道
          -r 可讀
          -s 非空
          -S 套接字
          -w 可寫(xiě)
          -N 從上次讀取之后已經(jīng)做過(guò)修改

          除了上面的單目測(cè)試,還可以使用表 2 所示的雙目操作符比較兩個(gè)文件:

          表 2. 測(cè)試一對(duì)文件
          操作符 為 True 的情況
          -nt 測(cè)試 file1 是否比 file2 更新。修改日期將用于這次和下次比較。
          -ot 測(cè)試 file1 是否比 file2 舊。
          -ef 測(cè)試 file1 是不是 file2 的硬鏈接。

          其他一些測(cè)試可以用來(lái)測(cè)試文件許可之類(lèi)的內(nèi)容。請(qǐng)參閱 bash 手冊(cè)獲得更多細(xì)節(jié)或使用 help test 查看內(nèi)置測(cè)試的簡(jiǎn)要信息。也可以用 help 命令了解其他內(nèi)置命令。

          -o 操作符允許測(cè)試?yán)?set -o 選項(xiàng) 設(shè)置的各種 shell 選項(xiàng),如果設(shè)置了該選項(xiàng),則返回 True (0),否則返回 False (1),如清單 3 所示。


          清單 3. 測(cè)試 shell 選項(xiàng)
          [ian@pinguino ~]$ set +o nounset
                                  [ian@pinguino ~]$ [ -o nounset ];echo $?
                                  1
                                  [ian@pinguino ~]$ set -u
                                  [ian@pinguino ~]$ test  -o nounset; echo $?
                                  0
                                  

          最后,-a-o 選項(xiàng)允許使用邏輯運(yùn)算符 AND 和 OR 將表達(dá)式組合在一起。單目操作符 ! 可以使測(cè)試的意義相反。可以用括號(hào)把表達(dá)式分組,覆蓋默認(rèn)的優(yōu)先級(jí)。請(qǐng)記住 shell 通常要在子 shell 中運(yùn)行括號(hào)中的表達(dá)式,所以需要用 \( 和 \) 轉(zhuǎn)義括號(hào),或者把這些操作符括在單引號(hào)或雙引號(hào)內(nèi)。清單 4 演示了摩根法則在表達(dá)式上的應(yīng)用。


          清單 4. 組合和分組測(cè)試
          [ian@pinguino ~]$ test "a" != "$HOME" -a 3 -ge 4 ; echo $?
                                  1
                                  [ian@pinguino ~]$ [ ! \( "a" = "$HOME" -o 3 -lt 4 \) ]; echo $?
                                  1
                                  [ian@pinguino ~]$ [ ! \( "a" = "$HOME" -o '(' 3 -lt 4 ')' ")" ]; echo $?
                                  1
                                  





          (( 和 [[

          test 命令非常強(qiáng)大,但是很難滿(mǎn)足其轉(zhuǎn)義需求以及字符串和算術(shù)比較之間的區(qū)別。幸運(yùn)的是,bash 提供了其他兩種測(cè)試方式,這兩種方式對(duì)熟悉 C、C++ 或 Java® 語(yǔ)法的人來(lái)說(shuō)會(huì)更自然些。

          (( )) 復(fù)合命令 計(jì)算算術(shù)表達(dá)式,如果表達(dá)式求值為 0,則設(shè)置退出狀態(tài)為 1;如果求值為非 0 值,則設(shè)置為 0。不需要對(duì) (()) 之間的操作符轉(zhuǎn)義。算術(shù)只對(duì)整數(shù)進(jìn)行。除 0 會(huì)產(chǎn)生錯(cuò)誤,但不會(huì)產(chǎn)生溢出。可以執(zhí)行 C 語(yǔ)言中常見(jiàn)的算術(shù)、邏輯和位操作。 let 命令也能執(zhí)行一個(gè)或多個(gè)算術(shù)表達(dá)式。它通常用來(lái)為算術(shù)變量分配值。


          清單 5. 分配和測(cè)試算術(shù)表達(dá)式
          [ian@pinguino ~]$ let x=2 y=2**3 z=y*3;echo $? $x $y $z
                                  0 2 8 24
                                  [ian@pinguino ~]$ (( w=(y/x) + ( (~ ++x) & 0x0f ) )); echo $? $x $y $w
                                  0 3 8 16
                                  [ian@pinguino ~]$ (( w=(y/x) + ( (~ ++x) & 0x0f ) )); echo $? $x $y $w
                                  0 4 8 13
                                  

          同使用 (( )) 一樣,利用復(fù)合命令 [[ ]] 可以對(duì)文件名和字符串使用更自然的語(yǔ)法。可以用括號(hào)和邏輯操作符把 test 命令支持的測(cè)試組合起來(lái)。


          清單 6. 使用 [[ 復(fù)合命令
          [ian@pinguino ~]$ [[ ( -d "$HOME" ) && ( -w "$HOME" ) ]] &&
                                  >  echo "home is a writable directory"
                                  home is a writable directory
                                  

          在使用 =!= 操作符時(shí),復(fù)合命令 [[ 還能在字符串上進(jìn)行模式匹配。匹配的方式就像清單 7 所示的通配符匹配。


          清單 7. 用 [[ 進(jìn)行通配符測(cè)試
          [ian@pinguino ~]$ [[ "abc def .d,x--" == a[abc]*\ ?d* ]]; echo $?
                                  0
                                  [ian@pinguino ~]$ [[ "abc def c" == a[abc]*\ ?d* ]]; echo $?
                                  1
                                  [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* ]]; echo $?
                                  1
                                  

          甚至還可以在 [[ 復(fù)合命令內(nèi)執(zhí)行算術(shù)測(cè)試,但是千萬(wàn)要小心。除非在 (( 復(fù)合命令內(nèi),否則 <> 操作符會(huì)把操作數(shù)當(dāng)成字符串比較并在當(dāng)前排序序列中測(cè)試它們的順序。清單 8 用一些示例演示了這一點(diǎn)。


          清單 8. 用 [[ 包含算術(shù)測(cè)試
          [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || (( 3 > 2 )) ]]; echo $?
                                  0
                                  [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 -gt 2 ]]; echo $?
                                  0
                                  [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 > 2 ]]; echo $?
                                  0
                                  [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || a > 2 ]]; echo $?
                                  0
                                  [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || a -gt 2 ]]; echo $?
                                  -bash: a: unbound variable
                                  





          條件測(cè)試

          雖然使用以上的測(cè)試和 &&|| 控制操作符能實(shí)現(xiàn)許多編程,但 bash 還包含了更熟悉的 “if, then, else” 和 case 結(jié)構(gòu)。學(xué)習(xí)完這些之后,將學(xué)習(xí)循環(huán)結(jié)構(gòu),這樣您的工具箱將真正得到擴(kuò)展。

          If、then、else 語(yǔ)句

          bash 的 if 命令是個(gè)復(fù)合命令,它測(cè)試一個(gè)測(cè)試或命令($?)的返回值,并根據(jù)返回值為 True(0)或 False(不為 0)進(jìn)行分支。雖然上面的測(cè)試只返回 0 或 1 值,但命令可能返回其他值。請(qǐng)參閱 LPI exam 102 prep: Shells, scripting, programming, and compiling 教程學(xué)習(xí)這方面的更多內(nèi)容。

          Bash 中的 if 命令有一個(gè) then 子句,子句中包含測(cè)試或命令返回 0 時(shí)要執(zhí)行的命令列表,可以有一個(gè)或多個(gè)可選的 elif 子句,每個(gè)子句可執(zhí)行附加的測(cè)試和一個(gè) then 子句,子句中又帶有相關(guān)的命令列表,最后是可選的 else 子句及命令列表,在前面的測(cè)試或 elif 子句中的所有測(cè)試都不為真的時(shí)候執(zhí)行,最后使用 fi 標(biāo)記表示該結(jié)構(gòu)結(jié)束。

          使用迄今為止學(xué)到的東西,現(xiàn)在能夠構(gòu)建簡(jiǎn)單的計(jì)算器來(lái)計(jì)算算術(shù)表達(dá)式,如清單 9 所示:


          清單 9. 用 if、then、else 計(jì)算表達(dá)式
          [ian@pinguino ~]$ function mycalc ()
                                  > {
                                  >   local x
                                  >   if [ $# -lt 1 ]; then
                                  >     echo "This function evaluates arithmetic for you if you give it some"
                                  >   elif (( $* )); then
                                  >     let x="$*"
                                  >     echo "$* = $x"
                                  >   else
                                  >     echo "$* = 0 or is not an arithmetic expression"
                                  >   fi
                                  > }
                                  [ian@pinguino ~]$ mycalc 3 + 4
                                  3 + 4 = 7
                                  [ian@pinguino ~]$ mycalc 3 + 4**3
                                  3 + 4**3 = 67
                                  [ian@pinguino ~]$ mycalc 3 + (4**3 /2)
                                  -bash: syntax error near unexpected token `('
                                  [ian@pinguino ~]$ mycalc 3 + "(4**3 /2)"
                                  3 + (4**3 /2) = 35
                                  [ian@pinguino ~]$ mycalc xyz
                                  xyz = 0 or is not an arithmetic expression
                                  [ian@pinguino ~]$ mycalc xyz + 3 + "(4**3 /2)" + abc
                                  xyz + 3 + (4**3 /2) + abc = 35
                                  

          這個(gè)計(jì)算器利用 local 語(yǔ)句將 x 聲明為局部變量,只能在 mycalc 函數(shù)的范圍內(nèi)使用。let 函數(shù)具有幾個(gè)可用的選項(xiàng),可以執(zhí)行與它密切關(guān)聯(lián)的 declare 函數(shù)。請(qǐng)參考 bash 手冊(cè)或使用 help let 獲得更多信息。

          如清單 9 所示,需要確保在表達(dá)式使用 shell 元字符 —— 例如(、)、*、> 和 < 時(shí) —— 正確地對(duì)表達(dá)式轉(zhuǎn)義。無(wú)論如何,現(xiàn)在有了一個(gè)非常方便的小計(jì)算器,可以像 shell 那樣進(jìn)行算術(shù)計(jì)算。

          在清單 9 中可能注意到 else 子句和最后的兩個(gè)示例。可以看到,把 xyz 傳遞給 mycalc 并沒(méi)有錯(cuò)誤,但計(jì)算結(jié)果為 0。這個(gè)函數(shù)還不夠靈巧,不能區(qū)分最后使用的示例中的字符值,所以不能警告用戶(hù)。可以使用字符串模式匹配測(cè)試(例如
          [[ ! ("$*" == *[a-zA-Z]* ]]
          ,或使用適合自己范圍的形式)消除包含字母表字符的表達(dá)式,但是這會(huì)妨礙在輸入中使用 16 進(jìn)制標(biāo)記,因?yàn)槭褂?16 進(jìn)制標(biāo)記時(shí)可能要用 0x0f 表示 15。實(shí)際上,shell 允許的基數(shù)最高為 64(使用 base#value 標(biāo)記),所以可以在輸入中加入 _ 和 @ 合法地使用任何字母表字符。8 進(jìn)制和 16 進(jìn)制使用常用的標(biāo)記方式,開(kāi)頭為 0 表示八進(jìn)制,開(kāi)頭為 0x 或 0X 表示 16 進(jìn)制。清單 10 顯示了一些示例。


          清單 10. 用不同的基數(shù)進(jìn)行計(jì)算
          [ian@pinguino ~]$ mycalc 015
                                  015 = 13
                                  [ian@pinguino ~]$ mycalc 0xff
                                  0xff = 255
                                  [ian@pinguino ~]$ mycalc 29#37
                                  29#37 = 94
                                  [ian@pinguino ~]$ mycalc 64#1az
                                  64#1az = 4771
                                  [ian@pinguino ~]$ mycalc 64#1azA
                                  64#1azA = 305380
                                  [ian@pinguino ~]$ mycalc 64#1azA_@
                                  64#1azA_@ = 1250840574
                                  [ian@pinguino ~]$ mycalc 64#1az*64**3 + 64#A_@
                                  64#1az*64**3 + 64#A_@ = 1250840574
                                  

          對(duì)輸入進(jìn)行的額外處理超出了本技巧的范圍,所以請(qǐng)小心使用這個(gè)計(jì)算器。

          elif 語(yǔ)句非常方便。它允許簡(jiǎn)化縮進(jìn),從而有助于腳本編寫(xiě)。在清單 11 中可能會(huì)對(duì) type 命令在 mycalc 函數(shù)中的輸出感到驚訝。


          清單 11. Type mycalc
          [ian@pinguino ~]$ type mycalc
                                  mycalc is a function
                                  mycalc ()
                                  {
                                  local x;
                                  if [ $# -lt 1 ]; then
                                  echo "This function evaluates arithmetic for you if you give it some";
                                  else
                                  if (( $* )); then
                                  let x="$*";
                                  echo "$* = $x";
                                  else
                                  echo "$* = 0 or is not an arithmetic expression";
                                  fi;
                                  fi
                                  }
                                  

          當(dāng)然,也可以只用 $(( 表達(dá)式 ))echo 命令進(jìn)行 shell 算術(shù)運(yùn)算,如清單 12 所示。這樣就不必學(xué)習(xí)關(guān)于函數(shù)或測(cè)試的任何內(nèi)容,但是請(qǐng)注意 shell 不會(huì)解釋元字符,例如 *,因此元字符不能在 (( 表達(dá)式 ))[[ 表達(dá)式 ]] 中那樣正常發(fā)揮作用。


          清單 12. 在 shell 中用 echo 和 $(( )) 直接進(jìn)行計(jì)算
          [ian@pinguino ~]$  echo $((3 + (4**3 /2)))
                                  35
                                  

          只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 安达市| 商丘市| 岗巴县| 呼图壁县| 托克逊县| 崇州市| 房山区| 济阳县| 德格县| 红河县| 葵青区| 博客| 葫芦岛市| 武冈市| 万宁市| 兴宁市| 乳山市| 辰溪县| 开封县| 兴化市| 浦东新区| 墨江| 保德县| 宜宾县| 柘荣县| 甘德县| 金昌市| 池州市| 行唐县| 枣阳市| 静乐县| 西畴县| 黄骅市| 潼南县| 铁岭市| 信宜市| 田东县| 锦屏县| 民勤县| 台安县| 德惠市|