linux的test命令(轉)

zhouwf0726發表於2019-07-08
每一種條件語句的基礎都是判斷什麼是真什麼是假。是否瞭解其工作原理將決定您編寫的是質量一般的指令碼還是您將引以為榮的指令碼。

Shell 指令碼的能力時常被低估,但實際上其能力的發揮受制於指令碼撰寫者的能力。您瞭解得越多,您就越能像變戲法似地撰寫一個檔案來使任務自動化和簡化您的管理工作。

在 shell 指令碼中進行的每一種操作(除最簡單的命令編組之外)都需要檢查條件。所有的 shell 指令碼“邏輯” — 廣義意義下的“邏輯” — 通常都可以分為以下三大類:

if {condition exists} then ...
while {condition exists} do ...
until {condition exists} do ...

無論隨後的操作是什麼,這些基於邏輯的命令都依靠判斷一種條件是否真實存在來決定後續的操作。test 命令是使得在每一種情況下都能夠確定要判斷的條件是否存在的實用工具。因此,徹底瞭解這個命令對於撰寫成功的 shell 指令碼至關重要。

工作原理

test 命令最短的定義可能是評估一個表示式;如果條件為真,則返回一個 0 值。如果表示式不為真,則返回一個大於 0 的值 — 也可以將其稱為假值。檢查最後所執行命令的狀態的最簡便方法是使用 $? 值。出於演示的目的,本文中的例子全部使用了這個引數。

test 命令期望在命令列中找到一個引數,當 shell 沒有為變數賦值時,則將該變數視為空。這意味著在處理指令碼時,一旦指令碼尋找的引數不存在,則 test 將報告該錯誤。

當試圖保護指令碼時,您可以通過將所有引數包含在雙引號中來解決這個問題。然後 shell 將變數展開,如果變數沒有值,那麼將傳遞一個空值給 test。另一種方法是在指令碼內增加一個額外檢查過程來判斷是否設定了命令列引數。如果沒有設定命令列引數,那麼指令碼會告訴使用者缺少引數,然後退出。我們會通過一些例子來更具體地說明所有這些內容。

test 和 [ 命令

雖然 Linux 和 UNIX 的每個版本中都包含 test 命令,但該命令有一個更常用的別名 — 左方括號:[。test 及其別名通常都可以在 /usr/bin 或 /bin (取決於作業系統版本和供應商)中找到。

當您使用左方括號而非 test 時,其後必須始終跟著一個空格、要評估的條件、一個空格和右方括號。右方括號不是任何東西的別名,而是表示所需評估引數的結束。條件兩邊的空格是必需的,這表示要呼叫 test,以區別於同樣經常使用方括號的字元/模式匹配操作。

test 和 [ 的語法如下:

test expression
[ expression ]

在這兩種情況下,test 都評估一個表示式,然後返回真或假。如果它和 if、while 或 until 命令結合使用,則您可以對程式流進行廣泛的控制。不過,您無需將 test 命令與任何其它結構一起使用;您可以從命令列直接執行它來檢查幾乎任何東西的狀態。

因為它們彼此互為別名,所以使用 test 或 [ 均需要一個表示式。表示式一般是文字、數字或檔案和目錄屬性的比較,並且可以包含變數、常量和運算子。運算子可以是字串運算子、整數運算子、檔案運算子或布林運算子 — 我們將在以下各部分依次介紹每一種運算子。

test 檔案運算子

利用這些運算子,您可以在程式中根據對檔案型別的評估結果執行不同的操作:

-b file 如果檔案為一個塊特殊檔案,則為真
-c file 如果檔案為一個字元特殊檔案,則為真
-d file 如果檔案為一個目錄,則為真
-e file 如果檔案存在,則為真
-f file 如果檔案為一個普通檔案,則為真
-g file 如果設定了檔案的 SGID 位,則為真
-G file 如果檔案存在且歸該組所有,則為真
-k file 如果設定了檔案的粘著位,則為真
-O file 如果檔案存在並且歸該使用者所有,則為真
-p file 如果檔案為一個命名管道,則為真
-r file 如果檔案可讀,則為真
-s file 如果檔案的長度不為零,則為真
-S file 如果檔案為一個套接字特殊檔案,則為真
-t fd 如果 fd 是一個與終端相連的開啟的檔案描述符(fd 預設為 1),則為真
-u file 如果設定了檔案的 SUID 位,則為真
-w file 如果檔案可寫,則為真
-x file 如果檔案可執行,則為真

以下示例顯示了此簡單操作的執行情況:

$ ls -l
total 33
drwxr-xr-w 2 root root 1024 Dec 5 05:05 LST
-rw-rw-rw- 1 emmett users 27360 Feb 6 07:30 evan
-rwsrwsrwx 1 root root 152 Feb 6 07:32 hannah
drwxr-xr-x 2 emmett users 1024 Feb 6 07:31 karen
-rw------- 1 emmett users 152 Feb 6 07:29 kristin
-rw-r--r-- 1 emmett users 152 Feb 6 07:29 spencer
$

$ test -r evan
$ echo $?
0

$ test -r walter
$ echo $?
1
$

由於第一次評估為真 — 檔案存在且可讀 — 返回值為真,或 0。由於第二次評估的檔案不存在,該值為假,返回值不為零。將值指定為零或非零很重要,因為在失敗時不會始終返回 1(雖然這是通常返回的值),可能返回一個非零值。

正如開頭所提到的,除了使用 test 外,您還可以用方括號 [ ] 將命令括住來向 shell 發出同樣的命令 — 如下所示:

$ [ -w evan ]
$ echo $?
0
$ [ -x evan ]
$ echo $?
1
$

同樣,第一個表示式為真,第二個表示式為假 — 正如返回值所指示的那樣。您還可以使用以下命令將兩個檔案彼此進行比較:

file1 -ef file2 測試以判斷兩個檔案是否與同一個裝置相連,是否擁有相同的 inode 編號
file1 -nt file2 測試以判斷第一個檔案是否比第二個檔案更新(由修改日期決定)
file1 -ot file2 測試以判斷第一個檔案是否比第二個檔案更舊

以下示例顯示了使用這些運算子比較檔案的結果:

$ [ evan -nt spencer ]
$ echo $?
0
$ [ karen -ot spencer ]
$ echo $?
1
$

名為 evan 的檔案比名為 spencer 的檔案更新,因而評估為真。類似地,名為 karen 的檔案比名為 spencer 的檔案更新,因此該評估為假。

字串比較運算子

如標題所示,這組函式比較字串的值。您可以檢查它們是否存在、是否相同或者是否不同。

String 測試以判斷字串是否不為空
-n string 測試以判斷字串是否不為空;字串必須為 test 所識別
-z string 測試以判斷字串是否為空;字串必須為 test 所識別
string1 = string2 測試以判斷 string1 是否與 string2 相同
string1 != string2 測試以判斷 string1 是否與 string2 不同

對任何變數進行的最有用的測試之一是判斷它的值是否不為空,可以簡單地將其放在 test 命令列中執行這種測試,如下例所示:

$ test "$variable"

強烈建議進行此種測試時用雙引號將變數括住,以讓 shell 識別變數(即使變數為空)。預設情況下執行的基本字串評估和 -n 測試從功能上講是相同的,如以下示例所示:

#example1
if test -n "$1"
then
echo "$1"
fi

執行以上例子中的程式碼將根據 $1 是否存在給出以下結果:

$ example1 friday
friday
$
$ example1
$

如果將程式碼更改為以下形式,則結果將相同:

#example2
if test "$1"
then
echo "$1"
fi

如下所示:

$ example2 friday
friday
$
$ example2
$

所有這些表明,通常不需要 -n,它代表預設操作。

要從一個不同的角度來檢視各種可能性,您可以用另一個選項來替換 -n,並檢查該值是否為空(相對於非空)。這可以用 -z 選項來實現,程式碼為:

#example3
if test -z "$1"
then
echo "no values were specified"
fi

執行如下:

$ example3
no values were specified
$ example3 friday
$

如果在沒有命令列引數的情況下執行該程式,而表示式評估為真,那麼將執行程式塊中的文字。如果在命令列中有值,則指令碼退出,不執行任何操作。將評估操作放在指令碼的開頭非常有用,這可以在可能產生錯誤的進一步處理之前預先檢查變數值。

其餘的字串運算子對兩個變數/字串之間的精確匹配或其中的差異(您也可以稱之為等價性和“不等價性”)進行評估。第一個例子對匹配進行測試:

$ env
LOGNAME=emmett
PAGER=less
SHELL=/bin/bash
TERM=linux
$
$ [ "$LOGNAME" = "emmett" ]
$ echo $?
0
$
$ [ "$LOGNAME" = "kristin" ]
$ echo $?
1
$

或者,該評估可以以指令碼的形式用於決定是否執行指令碼:

#example4
if [ "$LOGNAME" = "emmett" ]
then
echo "processing beginning"
else
echo "incorrect user"
fi

這種方法可以用來尋找任意的值(如終端型別或 shell 型別),在允許指令碼執行之前這些值必須匹配。請注意,= 或 != 運算子的優先順序高於其它大多數可指定選項,且要求必須伴有表示式。因此,除了比較字串的選項之外,= 或 != 都不能和檢查某種東西(如可讀檔案、可執行檔案或目錄)的存在性的選項一起使用。

整數比較運算子

正如字串比較運算子驗證字串相等或不同一樣,整數比較運算子對數字執行相同的功能。如果變數的值匹配則表示式測試為真,如果不匹配,則為假。整數比較運算子不處理字串(正如字串運算子不處理數字一樣):

int1 -eq int2 如果 int1 等於 int2,則為真
int1 -ge int2 如果 int1 大於或等於 int2,則為真
int1 -gt int2 如果 int1 大於 int2,則為真
int1 -le int2 如果 int1 小於或等於 int2,則為真
int1 -lt int2 如果 int1 小於 int2,則為真
int1 -ne int2 如果 int1 不等於 int2,則為真

以下示例顯示了一個程式碼段,其中在命令列中給出的值必須等於 7:

#example5
if [ $1 -eq 7 ]
then
echo "You've entered the magic number."
else
echo "You've entered the wrong number."
fi

執行中:

$ example5 6
You've entered the wrong number.
$
$ example5 7
You've entered the magic number.
$

和字串一樣,比較的值可以是在指令碼外為變數賦的值,而不必總是在命令列中提供。以下示例演示了實現這一點的一種方法:

#example6
if [ $1 -gt $number ]
then
echo "Sorry, but $1 is too high."
else
echo "$1 will work."
fi

$ set number=7
$ export number
$ example6 8
Sorry, but 8 is too high.
$ example6 7
7 will work.
$

整數比較運算子最佳的用途之一是評估指定的命令列變數的數目,並判斷它是否符合所要求的標準。例如,如果某個特定的命令只能在有三個或更少變數的情況下執行,

#example7 - display variables, up to three
if [ "$#" -gt 3 ]
then
echo "You have given too many variables."
exit $#
fi

只要指定三個或更少的變數,該示例指令碼將正常執行(並返回值 0)。如果指定了三個以上的變數,則將顯示錯誤訊息,且例程將退出 — 同時返回與命令列中給定的變數數相等的退出程式碼。

對這個過程進行修改可以用來在允許執行報表之前判斷當天是否是本月的最後幾天:

#example8 - to see if it is near the end of the month#
set `date` # use backward quotes
if [ "$3" -ge 21 ]
then
echo "It is close enough to the end of the month to proceed"
else
echo "This report cannot be run until after the 21st of the month"
exit $3
fi

在這個例子中,設定了六個變數(通過空格彼此分開):

$1 = Fri
$2 = Feb
$3 = 6
$4 = 08:56:30
$5 = EST
$6 = 2004

這些值可以在指令碼中使用,就像它們是在命令列中輸入的一樣。請注意,退出命令再次返回一個值 — 在這種情況下,返回的值是從 $3 的值中得到的日期。這一技巧在故障診斷時會非常有用 — 如果您認為指令碼應該執行而沒有執行,那麼請檢視 $? 的值。

一種類似的想法可能是撰寫一個只在每個月的第三個星期三執行的指令碼。第三個星期三一定在該月的 15 日到 21 日之間。使用 cron,您可以呼叫指令碼在 15 日到 21 日之間每天的一個指定時間執行,然後使用指令碼的第一行檢查 $1(在設定日期之後)的值是否為 Thu。如果為 Thu,那麼執行剩下的指令碼,如果不是,則退出。

而另一個想法可能是,只允許指令碼在超過 6:00 p.m. (18:00),所有使用者都回家之後執行。只要撰寫指令碼,使其在值低於 18 時退出,並通過使用以下命令來獲取時間(將其設為 $1)

set `date +%H`

布林運算子

布林運算子在幾乎每種語言中的工作方式都相同 — 包括 shell 指令碼。在 nutshell 中,它們檢查多個條件為真或為假,或者針對假的條件而不是真的條件採取操作。與 test 搭配使用的運算子有

! expr 如果表示式評估為假,則為真
expr1 -a expr2 如果 expr1 和 expr2 評估為真,則為真
expr1 -o expr2 如果 expr1 或 expr2 評估為真,則為真

可以用 != 運算子代替 = 進行字串評估。這是最簡單的布林運算子之一,對 test 的正常結果取非。

其餘兩個運算子中的第一個是 -a(即 AND)運算子。要使測試最終為真,兩個表示式都必須評估為真。如果任何一個評估為假,則整個測試將評估為假。例如,

$ env
HOME=/
LOGNAME=emmett
MAIL=/usr/mail/emmett
PATH=:/bin:/usr/bin:/usr/lbin
TERM=linux
TZ=EST5:0EDT
$
$ [ "$LOGNAME" = "emmett" -a "$TERM" = "linux" ]
$ echo $?
0
$

$ [ "LOGNAME" = "karen" -a "$TERM" = "linux" ]
$ echo $?
1
$

在第一個評估中,兩個條件都測試為真(在一個 linux 終端上登入的是 emmett),因此整個評估為真。在第二個評估中,終端檢查正確但使用者不正確,因此整個評估為假。

簡而言之,AND 運算子可以確保程式碼只在兩個條件都滿足時才執行。相反,只要任何一個表示式測試為真,OR (-o) 運算子即為真。我們來修改先前的例子,並將其放到一個指令碼中來說明這一點:

#example9
if [ "$LOGNAME" = "emmett" -o "$TERM" = "linux" ]
then
echo "Ready to begin."
else
echo "Incorrect user and terminal."
fi

$ env
HOME=/
LOGNAME=emmett
MAIL=/usr/mail/emmett
PATH=:/bin:/usr/bin:/usr/lbin
TERM=linux
TZ=EST5:0EDT
$ example9
Ready to begin.
$
$ LOGNAME=karen
$ example9
Ready to begin.
$

在指令碼第一次執行時,評估判斷使用者是否等於 emmett。如果發現使用者等於 emmett,則指令碼轉至 echo 語句,並跳過其餘的檢查。它從不檢查終端是否等於 linux,因為它只需要找到一條為真的語句就可以使整個運算為真。在指令碼第二次執行時,它判斷使用者不是 emmett,因此它將檢查並發現終端確實是 linux。由於一個條件為真,指令碼現在轉至 echo 命令。為了引出第二條訊息,兩個條件都必須為假。

在先前確定時間是否為月末的例子中,可以執行類似的檢查來防止使用者試圖在週末執行指令碼:

#example10 - Do not let the script run over the weekend#
set `date` # use backward quotes
if [ "$1" = "Sat" -o "$1" = "Sun" ]
then
echo "This report cannot be run over the weekend."

fi

一些有用的示例

示例 1:在指令碼檔案中出現的“邏輯”的最簡單的形式(如本文所有示例中所示)是“if ... then”語句。先前的一個程式碼段檢查是否存在一定數量的變數,然後將這些變數回顯。假設我們對此稍微做一些修改,比如我們想回顯變數,並且每次回顯均減去最左邊的變數,以顯示一個倒的三角形。

雖然這聽起來很簡單,但實際並非如此;這是您在執行大規模處理時想實現的方式:處理第一個變數、轉移、處理下一個變數……

出於演示的目的,可以按以下方式撰寫指令碼中的重要行:

#example11 - display declining variables, up to three
if [ "$#" -gt 3 ] # see if more than three variables are given
then
echo "You have given more than three variables."

exit
fi
echo $*
if test -n "$2"
then
shift
echo $*
fi
if test -n "$2"
then
shift
echo $*
fi

它將按以下方式執行:

$ example11 one
one
$

$ example11 one two
one two
two
$

$ example11 one two three
one two three
two three
three
$

$ example11 one two three four
You have given more than three variables.
$

出於檢查的目的將數量限制為三個變數的原因是減少在例子中要檢查的行數。一切都按部就班地進行,雖然它令人難以置信地混亂;使用者因使用了超過程式依設計所能處理的變數數而得到警告,且指令碼退出。如果變數數為 3 或更少,則運算的核心部分開始執行。

回顯變數,執行測試以檢視另一個變數是否存在。如果另一個變數存在,則執行一次轉移,回顯該變數,執行另一測試,等等。總共使用了 16 個有效行,而程式僅能處理不超過三個變數 — 非常混亂。假設消除變數數的限制,程式可以處理任意數量的變數。經過一些修改,指令碼被縮短(美化)了,並能處理任意數量的變數:

#example12 - display declining variables, any number
while [ "$#" -gt 0 ]
do
echo $*
shift
done

$ example12 1 2 3 4 5 6 7 8 9 0
1 2 3 4 5 6 7 8 9 0
2 3 4 5 6 7 8 9 0
3 4 5 6 7 8 9 0
4 5 6 7 8 9 0
5 6 7 8 9 0
6 7 8 9 0
7 8 9 0
8 9 0
9 0
0

現在減少到只有 5 個有效行,且消除了第一個指令碼三個變數的限制,並在執行時要更高效。

示例 2:無論何時當在指令碼內執行與處理相關的操作時,下一個操作將始終檢查上一操作的狀態,以確認它已成功完成。您可以通過檢查 $? 的狀態並驗證它等於 0 來實現這一目的。例如,如果一個資料目錄是否能訪問非常重要,

#example13
TEMP=LST
cd $TEMP
if [ $?-ne 0 ]
then
echo "Data directory could not be found."
Exit
fi

處理錯誤

資源

下載針對 Linux 的 Oracle 資料庫 10g
Oracle 資料庫 10g 第 1 版 (10.1.0.2) 現在可用於 Linux x86 和 Linux Itanium 平臺;請在此從 OTN 上免費下載。

訪問 Linux 技術中心
收藏本頁,以獲取關於 Linux 系統管理員最佳應用的一般技術資訊,以及關於 Oracle-on-Linux 產品群的具體技術資訊。

相關文章

Linux 相關技術文章的存檔

test 命令常常出現的錯誤事實上只有兩種型別。第一種是未使用正確的評估型別,例如將字串變數與整型變數進行比較或者將帶填充的字串與不帶填充的字串進行比較。仔細評估您使用的變數將使您最終找到錯誤的根源,並讓您能夠解決這些問題。

第二種錯誤型別包括將方括號誤認為別名之外的某個東西。方括號與其內容之間必須有一個空格;否則,它們將不能解釋其中的物件。例如,

$ [ "$LOGNAME" -gt 9]
test:] missing
$

請注意,錯誤訊息指示 test 存在問題,即使使用了別名 ]。這些問題很容易發現,因為錯誤訊息準確地將這些問題顯示出來,然後您可以增加必要的空格。

結論

要在 shell 指令碼中構建邏輯,您必須新增條件語句。每一條這種語句的核心都是對條件的評估,以判斷它是否存在 — 通過使用 test 命令完成評估。瞭解它和它的別名(左方括號 ([)的工作原理將使您能夠撰寫可以完成一些複雜操作的 shell 指令碼。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/756652/viewspace-242097/,如需轉載,請註明出處,否則將追究法律責任。

相關文章