本篇內(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)問題。另一個問題是栖秕,這些比較都不會先檢查文件是否存在春塌。