Bash技巧:對比 test、[、[[ 判斷字符串是否為空的用法


Bash技巧:對比 test、[、[[ 判斷字符串是否為空的用法

在 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 ]] 會執行報錯。


分享到:


相關文章: