在 Linux bash shell 中,test 命令、[ 命令、[[ 命令都可以用於進行一些判斷。
例如,這三個命令都可以用來判斷字符串是否為空。
實際使用時,這幾個命令的用法有一些異同和一些注意事項。
具體說明如下。
test 命令 和 [ 命令的關係
在 bash 中,[ 關鍵字本身是一個命令,它不是 if 命令的一部分。
執行 help [ 命令,有如下說明:
[: [ arg... ]
Evaluate conditional expression.
This is a synonym for the "test" builtin, but the last argument must be a literal ], to match the opening [.
即,[ 命令是 test 命令的同義詞,它對條件表達式的判斷結果和 test 命令完全一樣。
但是 [ 命令要求該命令的最後一個參數必須是 ],看起來是閉合的方括號效果。
在實際使用中,test 命令 和 [ 命令常常跟 if 命令、while 命令結合使用。
但這並不是必須的。test 命令 和 [ 命令本身都是獨立的命令,可以單獨執行。
後面會統一用 test 命令來說明它的用法。這些說明都適用於 [ 命令。
注意:] 自身不是 bash 的命令,它只是 [ 命令要求的參數,且必須是最後一個參數。
注意:在使用 [ 命令時,最大的誤區是在這個命令之後沒有加空格。
例如 [string1 != string2] 這種寫法是錯誤的。
要時刻注意 [ 本身是一個命令,這個命令名就是 [。
在這個命令之後會跟著一些參數,要用空格把命令名和參數隔開。
[string1 這個寫法實際上會執行名為 [string1 的命令,不是執行 [ 命令。
類似的,] 本身是一個參數,它也要用空格來隔開其他參數。
string2] 這個寫法實際上是一個名為 "string2]" 的參數。
而不是 string2 和 ] 兩個參數。
用 test 命令判斷字符串是否為空
執行 help test 命令,有如下說明:
test: test [expr]
Evaluate conditional expression.
Exits with a status of 0 (true) or 1 (false) depending on
the evaluation of EXPR.
The behavior of test depends on the number of arguments.
Read the bash manual page for the complete specification.
String operators:
-z STRING: True if string is empty.
-n STRING/STRING: True if string is not empty.
即,test 命令使用 -z STRING 操作符來判斷 STRING 字符串的長度是否為 0。
如果為 0,就是空字符串,會返回 true。
具體寫法是 test -z STRING,使用 [ 命令則寫為 [ -z STRING ]。
-n STRING 操作符判斷 STRING 字符串的長度是否為 0。
如果不為 0,就不是空字符串,會返回 true。
具體寫法是 test -n STRING,使用 [ 命令則寫為 [ -n STRING ]。
可以省略 -n 操作符,直接寫為 test STRING、或者 [ STRING ]。
注意:在實際使用時,要注意下面幾點:
- 當判斷變量值對應的字符串是否為空時,一定要用雙引號把變量值括起來。否則變量值為空、或者帶有空格時,會返回異常的結果。
例如,要寫為 test -n "$string",不建議寫為 test -n $string。 - bash 是以 0 作為 true,以 1 作為 false。上面 test 命令的說明也是如此。
而大部分編程語言是以 1 作為 true,0 作為 false。要注意區分,避免搞錯判斷條件的執行關係。 - 上面 help test 的說明提到,test 命令的參數個數會影響它的行為。具體要參考 man bash 的說明。
不同的參數個數會導致 test 命令返回很多不預期的結果。
下面用一個 empty_string.sh 腳本來舉例說明 test 命令和 [ 命令判斷字符串是否為空的方法,其內容如下:
(不好意思,近期網頁版文章的代碼塊排版錯亂,後臺諮詢確認網頁版不支持。下面用四個 ‘----’ 代替四個空格來進行縮進和隔行顯示。後面的代碼塊會類似處理。如果需要複製代碼到本地驗證,麻煩以四個 ‘----’為單位,替換成四個空格。非常抱歉。)
#!/bin/bash
function empty_string()
{
----if test -n $1; then
--------echo '(1) -n $1 :' "No quote: not empty."
----fi
----
----if [ -z $1 ]; then
--------echo '(2) -z $1 :' "No quote: empty."
----fi
----
----if test -n "$1"; then
--------echo '(3) -n "$1":' "Quote : not empty."
----
fi----
----if [ -z "$1" ]; then
--------echo '(4) -z "$1":' "Quote : empty."
----fi
}
empty_string "$1"
這個腳本使用 test 命令的 -n、-z 操作符來判斷傳入腳本的第一個參數是否為空字符串,並對比加雙引號和不加雙引號把變量值括起來的測試結果。
具體執行結果如下:
$ ./empty_string.sh go
(1) -n $1 : No quote: not empty.
(3) -n "$1": Quote : not empty.
$ ./empty_string.sh "go on"
./empty_string.sh: line 5: test: go: binary operator expected
./empty_string.sh: line 9: [: go: binary operator expected
(3) -n "$1": Quote : not empty.
$ ./empty_string.sh
(1) -n $1 : No quote: not empty.
(2) -z $1 : No quote: empty.
(4) -z "$1": Quote : empty.
可以看到,執行 ./empty_string.sh go 命令,傳入的第一個參數值沒有包含空格,$1 變量值加不加雙引號的判斷結果都正確。
執行 ./empty_string.sh "go on" 命令,傳入的第一個參數值包含空格。
$1 變量值不加雙引號的語句執行報錯,提示 "binary operator expected"。
test 命令在 -n、-z 操作符後面預期只有一個參數。
而這裡的 test -n $1 擴展為 test -n test string。
在 -n 後面提供了兩個參數,加上 -n 總共是三個參數,導致執行報錯。
使用雙引號把 $1 變量值括起來,整個變量值就會被當成一個參數,執行 test -n "$1" 命令不會報錯。
執行 ./empty_string.sh 命令,沒有提供第一個參數,測試結果比較奇怪。
-n $1 認為 $1 不為空。
而 -z $1 又認為 $1 為空。
只有 -z "$1" 正確地判斷出第一個參數值為空。
原因在於,沒有提供第一個參數時,這裡的 $1 的值是空,相當於什麼都沒有。
test -n $1 語句經過 bash 處理後,得到的是 test -n。
[ -z $1 ] 語句經過 bash 處理後,得到的是 [ -z ],相當於 test -z。
test -n 和 test -z 的返回結果都是 true。
所以才打印出來 $1 即為空,又不為空,判斷結果不符合預期。
可以再次看到,-z "$1" 用雙引號把變量值括起來,得到了預期的判斷結果。
添加雙引號可以避免很多異常的現象。
使用 bash -x ./empty_string.sh 打印執行腳本時的調試信息,可以看到 [ -z $1 ] 和 [ -z "$1" ] 擴展結果的區別:
$ bash -x ./empty_string.sh
+ empty_string ''
+ test -n
+ echo '(1) -n $1 :' 'No quote: not empty.'
(1) -n $1 : No quote: not empty.
+ '[' -z ']'
+ echo '(2) -z $1 :' 'No quote: empty.'
(2) -z $1 : No quote: empty.
+ test -n ''
+ '[' -z '' ']'
+ echo '(4) -z "$1":' 'Quote : empty.'
(4) -z "$1": Quote : empty.
結合上面的代碼,可以看到 [ -z $1 ] 擴展得到的調試信息是 '[' -z ']',在 -z 後面沒有任何參數。
而 [ -z "$1" ] 擴展得到的結果是 '[' -z '' ']'。
在 -z 後有一個參數 '',這個參數的值是空字符串。
用 [[ 命令判斷字符串是否為空
查看 help [[ 對 [[ 命令說明如下:
[[ ... ]]: [[ expression ]]
Execute conditional command.
Returns a status of 0 or 1 depending on the evaluation of the conditional
expression EXPRESSION. Expressions are composed of the same primaries used
by the 'test' builtin, and may be combined using the following operators:
( EXPRESSION ): Returns the value of EXPRESSION
! EXPRESSION: True if EXPRESSION is false; else false
EXPR1 && EXPR2: True if both EXPR1 and EXPR2 are true; else false
EXPR1 || EXPR2: True if either EXPR1 or EXPR2 is true; else false
即,[[ 命令可以使用 test 命令所支持的條件表達式來進行判斷。
它們之間的一些區別具體說明如下。
上面提到,[ 命令要求最後一個參數必須是 ],] 本身不是一個命令。
類似的,[[ 命令也要求跟 ]] 同時出現。但是 ]] 本身也是一個命令,而不是一個參數。
所以 [[ expression ]] 被稱為複合命令 (compound command)。
如下面例子所示:
$ ]
]: command not found
$ ]]
-bash: syntax error near unexpected token `]]'
可以看到,試圖執行 ] 命令,提示命令沒有找到,說明沒有這個命令。
而執行 ]] 命令,沒有提示找不到命令,只是提示語法錯誤,預期在該命令之前要有 [[ 命令。
由於 [[ 和 ]] 都是命令,需要用空格把它們和其他參數隔開。
查看 man bash 裡面對 [[ 有如下說明:
Word splitting and pathname expansion are not performed on the words between the [[ and ]]; tilde expansion, parameter and variable expansion, arithmetic expansion, command substitution, process substitution, and quote removal are performed.
即,在 [[ 和 ]] 裡面引用變量值時,不會對變量值進行單詞拆分 (Word splitting)。
即使變量值帶有空格,不用雙引號括起來也不會被拆分成多個參數。
而 test 命令 和 [ 命令會進行單詞拆分,可能會導致參數個數發生變化。
可以參考前面幾個例子的說明。
使用 [[ 判斷字符串是否為空的一些例子如下所示:
$ value=
$ [[ -n $value ]]; echo $?
1
$ [[ -z $value ]]; echo $?
0
$ value="go on"
$ [[ -n $value ]]; echo $?
0
$ [[ -n go on ]]; echo $?
-bash: syntax error in conditional expression
-bash: syntax error near 'on'
可以看到,將 value 變量值設成空,[[ -n $value ]] 返回為 1,確認該變量值不為空是 false。
[[ -z $value ]] 返回為 0,確認該變量值為空是 true。
即使 $value 不加雙引號,也能正確判斷。
如果是用 [ 命令就會判斷異常。
當 value 變量值包含空格時,[[ -n $value ]] 可以正確判斷,但是如果直接寫為 [[ -n go on ]] 會執行報錯。
閱讀更多 霜魚片 的文章