結(jié)構(gòu)化命令 之 test命令(長文慎入)

本篇內(nèi)容均摘自《Linux命令行與shell腳本編程大全》,個人認為需要重點學(xué)習(xí)的章節(jié)纤勒。【免費】Linux命令行與Shell腳本編程大全 第3版 PDF全本 21MB 百度網(wǎng)盤下載 - 今夕是何夕 - 博客園
到目前為止,在if語句中看到的都是普通shell命令隆檀。你可能想問摇天, if-then語句是否能測試命令退出狀態(tài)碼之外的條件粹湃。答案是不能。但在bash shell中有個好用的工具可以幫你通過if-then語句測試其他條件泉坐。test命令提供了在if-then語句中測試不同條件的途徑为鳄。如果test命令中列出的條件成立,test命令就會退出并返回退出狀態(tài)碼0腕让。這樣if-then語句就與其他編程語言中的if-then語句以類似的方式工作了孤钦。如果條件不成立, test命令就會退出并返回非零的退出狀態(tài)碼纯丸,這使得if-then語句不會再被執(zhí)行偏形。test命令的格式非常簡單:test condition。
condition是test命令要測試的一系列參數(shù)和值觉鼻。當(dāng)用在if-then語句中時俊扭, test命令看起來是這樣的。
if test condition
then
commands
fi
如果不寫test命令的condition部分坠陈,它會以非零的退出狀態(tài)碼退出萨惑, 并執(zhí)行else語句塊。

$ cat test6.sh
#!/bin/bash
if test
then
  echo "No expression returns a True"
else
  echo "No expression returns a False"
fi
$ ./test6.sh
No expression returns a False

當(dāng)你加入一個條件時仇矾, test命令會測試該條件庸蔼。例如,可以使用test命令確定變量中是否有內(nèi)容贮匕。這只需要一個簡單的條件表達式朱嘴。

$ cat test6.sh
#!/bin/bash
my_variable="Full"
if test $my_variable
then
  echo "The $my_variable expression returns a True"
else
  echo "The $my_variable expression returns a False"
fi
$ ./test6.sh
The Full expression returns a True

變量my_variable中包含有內(nèi)容( Full),因此當(dāng)test命令測試條件時粗合,返回的退出狀態(tài)為0萍嬉。這使得then語句塊中的語句得以執(zhí)行。如果該變量中沒有包含內(nèi)容隙疚,就會出現(xiàn)相反的情況:

$ cat test6.sh
#!/bin/bash
my_variable=""
if test $my_variable
then
  echo "The $my_variable expression returns a True"
else
  echo "The $my_variable expression returns a False"
fi
$ ./test6.sh
The expression returns a False

bash shell提供了另一種條件測試方法壤追,無需在if-then語句中聲明test命令。
if [ condition ]
then
commands
fi
方括號定義了測試條件供屉。注意行冰,第一個方括號之后和第二個方括號之前必須加上一個空格,否則就會報錯伶丐。test命令可以判斷三類條件:數(shù)值比較悼做,字符串比較,文件比較哗魂。

數(shù)值比較:使用test命令最常見的情形是對兩個數(shù)值進行比較肛走。

$ cat numeric_test.sh
#!/bin/bash
value1=10
value2=11
if [ $value1 -gt 5 ]#測試變量value1的值是否大于5。
then
  echo "The test value $value1 is greater than 5"
fi
#
if [ $value1 -eq $value2 ]#測試變量value1的值是否和變量value2的值相等录别。
then
  echo "The values are equal"
else
  echo "The values are different"
fi

兩個數(shù)值條件測試的結(jié)果和預(yù)想一致朽色。

$ ./numeric_test.sh
The test value 10 is greater than 5
The values are different

但是涉及浮點值時邻吞,數(shù)值條件測試會有一個限制。

$ cat floating_point_test.sh
#!/bin/bash
value1=5.555
echo "The test value is $value1"
if [ $value1 -gt 5 ]
then
  echo "The test value $value1 is greater than 5"
fi
$ ./floating_point_test.sh
The test value is 5.555
./floating_point_test.sh: line 8:
[: 5.555: integer expression expected

此例葫男,變量value1中存儲的是浮點值抱冷。接著,腳本對這個值進行了測試梢褐。顯然這里出錯了旺遮。記住, bash shell只能處理整數(shù)盈咳。說明我們不能在test命令中使用浮點值趣效。

字符串比較
字符串的相等和不等條件不言自明,很容易看出兩個字符串值是否相同:

$ cat test7.sh
#!/bin/bash
testuser=rich
if [ $USER = $testuser ]
then
  echo "Welcome $testuser"
fi
$ ./test7.sh
Welcome rich

字符串不等條件也可以判斷兩個字符串是否有相同的值:

$ cat test8.sh
#!/bin/bash
testuser=baduser
if [ $USER != $testuser ]
then
  echo "This is not $testuser"
else
  echo "Welcome $testuser"
fi
$ ./test8.sh
This is not baduser

在比較字符串的相等性時猪贪,比較測試會將所有的標點和大小寫情況都考慮在內(nèi)。
要測試一個字符串是否比另一個字符串大就是麻煩的開始讯私。當(dāng)要開始使用測試條件的大于或小于功能時热押,就會出現(xiàn)兩個經(jīng)常困擾shell程序員的問題:
1.大于號和小于號必須轉(zhuǎn)義,否則shell會把它們當(dāng)作重定向符號斤寇,把字符串值當(dāng)作文件
名桶癣;
2.大于和小于順序和sort命令所采用的不同。
在編寫腳本時娘锁,第一條可能會導(dǎo)致一個不易察覺的嚴重問題牙寞。下面的例子展示了shell腳本編程初學(xué)者時常碰到的問題。

$ cat badtest.sh
#!/bin/bash
val1=baseball
val2=hockey
if [ $val1 > $val2 ]
then
  echo "$val1 is greater than $val2"
else
  echo "$val1 is less than $val2"
fi
$ ./badtest.sh
baseball is greater than hockey
$ ls -l hockey
-rw-r--r-- 1 rich rich 0 Sep 30 19:08 hockey

這個腳本中只用了大于號莫秆,沒有出現(xiàn)錯誤间雀,但結(jié)果是錯的。腳本把大于號解釋成了輸出重定向(參見第15章)镊屎。因此惹挟,它創(chuàng)建了一個名為hockey的文件。由于重定向的順利完成缝驳, test命令返回了退出狀態(tài)碼0连锯, if語句便以為所有命令都成功結(jié)束了。要解決這個問題用狱,就需要正確轉(zhuǎn)義大于號:

$ cat test9.sh
#!/bin/bash
val1=baseball
val2=hockey
if [ $val1 \> $val2 ]
then
echo "$val1 is greater than $val2"
else
echo "$val1 is less than $val2"
fi
$ ./test9.sh
baseball is less than hockey

現(xiàn)在的答案已經(jīng)符合預(yù)期的了运怖。
第二個問題更細微,除非你經(jīng)常處理大小寫字母夏伊,否則幾乎遇不到摇展。 sort命令處理大寫字母的方法剛好跟test命令相反。讓我們在腳本中測試一下這個特性溺忧。

$ cat test9b.sh
#!/bin/bash
val1=Testing
val2=testing
if [ $val1 \> $val2 ]
then
  echo "$val1 is greater than $val2"
else
  echo "$val1 is less than $val2"
fi
$ ./test9b.sh
Testing is less than testing
$ sort testfile
testing
Testing

在比較測試中吗购,大寫字母被認為是小于小寫字母的医男。但sort命令恰好相反。當(dāng)你將同樣的字符串放進文件中并用sort命令排序時捻勉,小寫字母會先出現(xiàn)镀梭。這是由各個命令使用的排序技術(shù)不同造成的。比較測試中使用的是標準的ASCII順序踱启,根據(jù)每個字符的ASCII數(shù)值來決定排序結(jié)果报账。 sort命令使用的是系統(tǒng)的本地化語言設(shè)置中定義的排序順序。對于英語埠偿,本地化設(shè)置指定了在排序順序中小寫字母出現(xiàn)在大寫字母前透罢。

-n和-z可以檢查一個變量是否含有數(shù)據(jù):

$ cat test10.sh
#!/bin/bash
val1=testing
val2=''
if [ -n $val1 ] #判斷val1變量是否長度非0,而它的長度正好非0冠蒋,所以then部分被執(zhí)行了羽圃。
then
  echo "The string '$val1' is not empty"
else
  echo "The string '$val1' is empty"
fi
#
if [ -z $val2 ] #判斷val2變量是否長度為0,而它正好長度為0抖剿,所以then部分被執(zhí)行了朽寞。
then
  echo "The string '$val2' is empty"
else
  echo "The string '$val2' is not empty"
fi
#
if [ -z $val3 ] #判斷val3變量是否長度為0。這個變量并未在shell腳本中定義過斩郎,所以它的字符串長度仍然為0脑融,盡管它未被定義過。
then
  echo "The string '$val3' is empty"
else
  echo "The string '$val3' is not empty"
fi
$ ./test10.sh
The string 'testing' is not empty
The string '' is empty
The string '' is empty

文件比較(這里舉例了10個不同的比較情況)
1.檢查目錄
-d測試會檢查指定的目錄是否存在于系統(tǒng)中缩宜。如果你打算將文件寫入目錄或是準備切換到某個目錄中肘迎,先進行測試總是件好事情。

$ cat test11.sh
#!/bin/bash
jump_directory=/home/arthur
if [ -d $jump_directory ]
then
  echo "The $jump_directory directory exists"
  cd $jump_directory
  ls
else
  echo "The $jump_directory directory does not exist"
fi
$ ./test11.sh
The /home/arthur directory does not exist

2.檢查對象是否存在
-e允許你的腳本代碼在使用文件或目錄前先檢查它們是否存在锻煌。

$ cat test12.sh
#!/bin/bash
location=$HOME
file_name="sentinel"
if [ -e $location ]
then #目錄存在
  echo "OK on the $location directory."
  echo "Now checking on the file, $file_name."
    if [ -e $location/$file_name ]
    then #文件存在
      echo "OK on the filename"
      echo "Updating Current Date..."
      date >> $location/$file_name
    else #文件不存在
      echo "File does not exist"
      echo "Nothing to update"
    fi
else #目錄不存在
  echo "The $location directory does not exist."
  echo "Nothing to update"
fi
$ ./test12.sh
OK on the /home/Christine directory.
Now checking on the file, sentinel.
File does not exist
Nothing to update
$ touch sentinel
$ ./test12.sh
OK on the /home/Christine directory.
Now checking on the file, sentinel.
OK on the filename
Updating Current Date...

第一次檢查用-e比較來判斷用戶是否有HOME目錄妓布。如果有,接下來的-e比較會檢查sentinel文件是否存在于HOME目錄中宋梧。如果不存在秋茫, shell腳本就會提示該文件不存在,不需要進行更新乃秀。為確保更新操作能夠正常進行肛著,我們創(chuàng)建了sentinel文件,然后重新運行這個shell腳本跺讯。這一次在進行條件測試時枢贿, $HOME和sentinel文件都存在,因此當(dāng)前日期和時間就被追加到了文件中刀脏。
3.檢查文件
-e比較可用于文件和目錄局荚。要確定指定對象為文件,必須用-f比較。

$ cat test13.sh
#!/bin/bash
item_name=$HOME
echo "The item being checked: $item_name"
if [ -e $item_name ]
then #Item存在
  echo "The item, $item_name, does exist."
  echo "But is it a file?"
    if [ -f $item_name ]
    then #Item是個文件
      echo "Yes, $item_name is a file."
    else #Item不是文件
      echo "No, $item_name is not a file."
    fi
else #Item不存在
  echo "The item, $item_name, does not exist."
  echo "Nothing to update"
fi
$ ./test13.sh
The item being checked: /home/Christine
The item, /home/Christine, does exist.
But is it a file?
No, /home/Christine is not a file.

這一小段腳本進行了大量的檢查耀态!它首先使用-e比較測試$HOME是否存在轮傍。如果存在,繼續(xù)用-f來測試它是不是一個文件首装。如果它不是文件(當(dāng)然不會是了)创夜,就會顯示一條消息,表明這不是一個文件仙逻。
4.檢查是否可讀

$ cat test14.sh
#!/bin/bash
pwfile=/etc/shadow # 首先檢查文件是否存在
if [ -f $pwfile ]
then # 文件存在
  if [ -r $pwfile ]
    then #可讀的話
      tail $pwfile
    else #如果不可讀
      echo "Sorry, I am unable to read the $pwfile file"
   fi
else #文件不存在
  echo "Sorry, the file $file does not exist"
fi
$ ./test14.sh
Sorry, I am unable to read the /etc/shadow file

/etc/shadow文件含有系統(tǒng)用戶加密后的密碼驰吓,所以它對系統(tǒng)上的普通用戶來說是不可讀的。-r比較確定該文件不允許進行讀取系奉,因此測試失敗檬贰, bash shell執(zhí)行了if-then語句的else部分。
5.檢查空文件
應(yīng)該用-s比較來檢查文件是否為空缺亮,尤其是在不想刪除非空文件的時候翁涤。要留心的是,當(dāng)-s比較成功時萌踱,說明文件中有數(shù)據(jù)葵礼。

$ cat test15.sh
#!/bin/bash
file_name=$HOME/sentinel
if [ -f $file_name ]
then
    if [ -s $file_name ]
    then
      echo "The $file_name file exists and has data in it."
      echo "Will not remove this file."
    else
      echo "The $file_name file exists, but is empty."
      echo "Deleting empty file..."
      rm $file_name
    fi
else
  echo "File, $file_name, does not exist."
fi
$ ls -l $HOME/sentinel
-rw-rw-r--. 1 Christine Christine 29 Jun 25 05:32 /home/Christine/sentinel
$ ./test15.sh
The /home/Christine/sentinel file exists and has data in it.
Will not remove this file.

-f比較測試首先測試文件是否存在。如果存在虫蝶,由-s比較來判斷該文件是否為空【胛鳎空文件會被刪除能真。可以從ls –l的輸出中看出sentinel并不是空文件扰柠,因此腳本并不會刪除它粉铐。
6.查看文件是否可寫
-w比較會判斷你對文件是否有可寫權(quán)限。腳本test16.sh只是腳本test13.sh的修改版÷钡担現(xiàn)在不單檢查item_name是否存在蝙泼、是否為文件,還會檢查該文件是否有寫入權(quán)限劝枣。

$ cat test16.sh
#!/bin/bash
item_name=$HOME/sentinel
echo "The item being checked: $item_name"
echo "Yes, $item_name is a file."
echo "But is it writable?"
  if [ -w $item_name ]
    then #Item是可寫的
      echo "Writing current time to $item_name"
      date +%H%M >> $item_name
    else #Item不可寫
      echo "Unable to write to $item_name"
  fi
else #Item不是一個文件
  echo "No, $item_name is not a file."
fi
$ ls -l sentinel
-rw-rw-r--. 1 Christine Christine 0 Jun 27 05:38 sentinel
$ ./test16.sh
The item being checked: /home/Christine/sentinel
Yes, /home/Christine/sentinel is a file.
But is it writable?
Writing current time to /home/Christine/sentinel
$ cat sentinel
0543

變量item_name被設(shè)置成HOME/sentinel汤踏,該文件允許用戶進行寫入。因此當(dāng)腳本運行時舔腾, -w測試表達式會返回非零退出狀態(tài)溪胶,然后執(zhí)行then代碼塊,將時間戳寫入文件sentinel中稳诚。如果使用chmod關(guān)閉文件sentinel的用戶 寫入權(quán)限哗脖, -w測試表達式會返回非零的退出狀態(tài)碼,時間戳不會被寫入文件。

$ chmod u-w sentinel
$ ls -l sentinel
-r--rw-r--. 1 Christine Christine 5 Jun 27 05:43 sentinel
$ ./test16.sh
The item being checked: /home/Christine/sentinel
The item, /home/Christine/sentinel, does exist.
But is it a file?
Yes, /home/Christine/sentinel is a file.
But is it writable?
Unable to write to /home/Christine/sentinel

chmod命令可用來為讀者再次回授寫入權(quán)限才避。這會使得寫入測試表達式返回退出狀態(tài)碼0橱夭,并允許一次針對文件的寫入嘗試。
7. 檢查文件是否可以執(zhí)行
-x比較是判斷特定文件是否有執(zhí)行權(quán)限的一個簡單方法桑逝。雖然可能大多數(shù)命令用不到它棘劣,但如果你要在shell腳本中運行大量腳本,它就能發(fā)揮作用肢娘。

$ cat test17.sh
#!/bin/bash
if [ -x test16.sh ]
then
  echo "You can run the script: "
  ./test16.sh
else
  echo "Sorry, you are unable to execute the script"
fi
$ ./test17.sh
You can run the script:
[...]
$ chmod u-x test16.sh
$ ./test17.sh
Sorry, you are unable to execute the script

這段示例shell腳本用-x比較來測試是否有權(quán)限執(zhí)行test16.sh腳本呈础。如果有權(quán)限,它會運行這個腳本橱健。在首次成功運行test16.sh腳本后而钞,更改文件的權(quán)限。這次拘荡, -x比較失敗了臼节,因為你已經(jīng)沒有test16.sh腳本的執(zhí)行權(quán)限了。
8. 檢查所屬關(guān)系
-O比較可以測試出你是否是文件的屬主珊皿。

$ cat test18.sh
#!/bin/bash
if [ -O /etc/passwd ]
then
  echo "You are the owner of the /etc/passwd file"
else
  echo "Sorry, you are not the owner of the /etc/passwd file"
fi
$ ./test18.sh
Sorry, you are not the owner of the /etc/passwd file

這段腳本用-O比較來測試運行該腳本的用戶是否是/etc/passwd文件的屬主网缝。這個腳本是運行在普通用戶賬戶下的,所以測試失敗了蟋定。
9. 檢查默認屬組關(guān)系
-G比較會檢查文件的默認組粉臊,如果它匹配了用戶的默認組,則測試成功驶兜。(這部分我省略了扼仲,可以自行看書)
10. 檢查文件日期
最后一組方法用來對兩個文件的創(chuàng)建日期進行比較。這在編寫軟件安裝腳本時非常有用抄淑。有時候屠凶,你不會愿意安裝一個比系統(tǒng)上已有文件還要舊的文件。
-nt比較會判定一個文件是否比另一個文件新肆资。如果文件較新矗愧,那意味著它的文件創(chuàng)建日期更近。 -ot比較會判定一個文件是否比另一個文件舊郑原。如果文件較舊唉韭,意味著它的創(chuàng)建日期更早。

$ cat test20.sh
#!/bin/bash
if [ test19.sh -nt test18.sh ]
then
  echo "The test19 file is newer than test18"
else
  echo "The test18 file is newer than test19"
fi
#
if [ test17.sh -ot test19.sh ]
then
  echo "The test17 file is older than the test19 file"
fi
$ ./test20.sh
The test19 file is newer than test18
The test17 file is older than the test19 file
$ ls -l test17.sh test18.sh test19.sh
-rwxrw-r-- 1 rich rich 167 2014-07-30 16:31 test17.sh
-rwxrw-r-- 1 rich rich 185 2014-07-30 17:46 test18.sh
-rwxrw-r-- 1 rich rich 167 2014-07-30 17:50 test19.sh

用于比較文件路徑是相對你運行該腳本的目錄而言的犯犁。如果你要檢查的文件已經(jīng)移走纽哥,就會出現(xiàn)問題。另一個問題是栖秕,這些比較都不會先檢查文件是否存在春塌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子只壳,更是在濱河造成了極大的恐慌俏拱,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吼句,死亡現(xiàn)場離奇詭異锅必,居然都是意外死亡,警方通過查閱死者的電腦和手機惕艳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門搞隐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人远搪,你說我怎么就攤上這事劣纲。” “怎么了谁鳍?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵癞季,是天一觀的道長。 經(jīng)常有香客問我倘潜,道長绷柒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任涮因,我火速辦了婚禮废睦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘养泡。我一直安慰自己嗜湃,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布瓤荔。 她就那樣靜靜地躺著净蚤,像睡著了一般钥组。 火紅的嫁衣襯著肌膚如雪输硝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天程梦,我揣著相機與錄音点把,去河邊找鬼。 笑死屿附,一個胖子當(dāng)著我的面吹牛郎逃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挺份,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼褒翰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起优训,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤朵你,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后揣非,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抡医,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年早敬,在試婚紗的時候發(fā)現(xiàn)自己被綠了忌傻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡搞监,死狀恐怖水孩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腺逛,我是刑警寧澤荷愕,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站棍矛,受9級特大地震影響安疗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜够委,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一荐类、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茁帽,春花似錦玉罐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至铁追,卻和暖如春季蚂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背琅束。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工扭屁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涩禀。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓料滥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親艾船。 傳聞我的和親對象是個殘疾皇子葵腹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容