# 7.3 其他比較操作
二元比較操作可以比較變量或者數量。
需要注意的是,整數和字符串比較使用的是兩套不同的操作符。
## 整數比較
### -eq
等于
`if [ "$a" -eq "$b" ]`
### -ne
不等于
`if [ "$a" -ne "$b" ]`
### -gt
大于
`if [ "$a" -gt "$b" ]`
### -ge
大于等于
`if [ "$a" -ge "$b" ]`
### -lt
小于
`if [ "$a" -lt "$b" ]`
### -le
小于等于
`if [ "$a" -le "$b" ]`
### <
小于(使用 [雙圓括號](http://tldp.org/LDP/abs/html/dblparens.html))
`(("$a" < "$b"))`
### <=
小于等于(使用雙圓括號)
`(("$a" <= "$b"))`
### >
大于(使用雙圓括號)
`(("$a" > "$b"))`
### >=
大于等于(使用雙圓括號)
`(("$a" >= "$b"))`
## 字符串比較
### =
等于
`if [ "$a" = "$b" ]`
 注意在`=`前后要加上[空格](http://tldp.org/LDP/abs/images/caution.gif)
`if [ "$a"="$b" ]` 和上面不等價。
### ==
等于
`if [ "$a" == "$b" ]`
和 `=` 同義
 `==` 操作符在 [雙方括號](http://tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETS) 和單方括號里的功能是不同的。
```bash
[[ $a == z* ]] # $a 以 "z" 開頭時為真(模式匹配)
[[ $a == "z*" ]] # $a 等于 z* 時為真(字符匹配)
[ $a == z* ] # 發生文件匹配和字符分割。
[ "$a" == "z*" ] # $a 等于 z* 時為真(字符匹配)
# 感謝 Stéphane Chazelas
```
### !=
不等于
`if [ "$a" != "$b" ]`
在 [`[[ ... ]]`](http://tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETS) 結構中會進行模式匹配。
### <
小于,按照 [ASCII碼](http://tldp.org/LDP/abs/html/special-chars.html#ASCIIDEF) 排序。
`if [[ "$a" < "$b" ]]`
`if [ "$a" \< "$b" ]`
注意在 `[]` 結構里 `<` 需要被 [轉義](http://tldp.org/LDP/abs/html/escapingsection.html#ESCP)。
### >
大于,按照 ASCII 碼排序。
`if [[ "$a" > "$b" ]]`
`if [ "$a" \> "$b" ]`
注意在 `[]` 結構里 `>` 需要被轉義。
[樣例 27-11](http://tldp.org/LDP/abs/html/arrays.html#BUBBLE) 包含了比較操作符。
### -z
字符串為空,即字符串長度為0。
```bash
String='' # 長度為0的字符串變量。
if [ -z "$String" ]
then
echo "\$String is null."
else
echo "\$String is NOT null."
fi # $String is null.
```
### -n
字符串非空(`null`)。
 使用 `-n` 時字符串必須是在括號中且被引用的。使用 `! -z` 判斷未引用的字符串或者直接判斷([樣例 7-6](http://tldp.org/LDP/abs/html/comparison-ops.html#STRTEST))通常可行,但是非常危險。判斷字符串時一定要引用[^1]。
樣例 7-5. 算術比較和字符串比較
```bash
#!/bin/bash
a=4
b=5
# 這里的 "a" 和 "b" 可以是整數也可以是字符串。
# 因為 Bash 的變量是弱類型的,因此字符串和整數比較有很多相同之處。
# 在 Bash 中可以用處理整數的方式來處理全是數字的字符串。
# 但是謹慎使用。
echo
if [ "$a" -ne "$b" ]
then
echo "$a is not equal to $b"
echo "(arithmetic comparison)"
fi
echo
if [ "$a" != "$b" ]
then
echo "$a is not equal to $b."
echo "(string comparison)"
# "4" != "5"
# ASCII 52 != ASCIII 53
fi
# 在這個例子里 "-ne" 和 "!=" 都可以。
echo
exit 0
```
樣例 7-6. 測試字符串是否為空(`null`)
```bash
#!/bin/bash
# str-test.sh: 測試是否為空字符串或是未引用的字符串。
# 使用 if [ ... ] 結構
# 如果字符串未被初始化,則其值是未定義的。
# 這種狀態就是空 "null"(并不是 0)。
if [ -n $string1 ] # 并未聲明或是初始化 string1。
then
echo "String \"string1\" is not null."
else
echo "String \"string1\" is null."
fi
# 盡管沒有初始化 string1,但是結果顯示其非空。
echo
# 再試一次。
if [ -n "$string1" ] # 這次引用了 $string1。
then
echo "String \"string1\" is not null."
else
echo "String \"string1\" is null."
fi # 在測試括號內引用字符串得到了正確的結果。
echo
if [ $string1 ] # 這次只有一個 $string1。
then
echo "String \"string1\" is not null."
else
echo "String \"string1\" is null."
fi # 結果正確。
# 獨立的 [ ... ] 測試操作符可以用來檢測字符串是否為空。
# 最好將字符串進行引用(if [ "$string1" ])。
#
# Stephane Chazelas 指出:
# if [ $string1 ] 只有一個參數 "]"
# if [ "$string1" ] 則有兩個參數,空的 "$string1" 和 "]"
echo
string1=initialized
if [ $string1 ] # $string1 這次仍然沒有被引用。
then
echo "String \"string1\" is not null."
else
echo "String \"string1\" is null."
fi # 這次的結果仍然是正確的。
# 最好將字符串引用("$string1")
string1="a = b"
if [ $string1 ] # $string1 這次仍然沒有被引用。
then
echo "String \"string1\" is not null."
else
echo "String \"string1\" is null."
fi # 這次沒有引用就錯了。
exit 0 # 同時感謝 Florian Wisser 的提示。
```
樣例 7-7. `zmore`
```bash
#!/bin/bash
# zmore
# 使用篩選器 'more' 查看 gzipped 文件。
E_NOARGS=85
E_NOTFOUND=86
E_NOTGZIP=87
if [ $# -eq 0 ] # 作用和 if [ -z "$1" ] 相同。
# $1 可以為空: zmore "" arg2 arg3
then
echo "Usage: `basename $0` filename" >&2
# 將錯誤信息通過標準錯誤 stderr 進行輸出。
exit $E_NOARGS
# 腳本的退出狀態為 85.
fi
filename=$1
if [ ! -f "$filename" ] # 引用字符串以防字符串中帶有空格。
then
echo "File $filename not found!" >&2 # 通過標準錯誤 stderr 進行輸出。
exit $E_NOTFOUND
fi
if [ ${filename##*.} != "gz" ]
# 在括號內使用變量代換。
then
echo "File $1 is not a gzipped file!"
exit $E_NOTGZIP
fi
zcat $1 | more
# 使用篩選器 'more'
# 也可以用 'less' 替代
exit $? # 腳本的退出狀態由管道 pipe 的退出狀態決定。
# 實際上 "exit $?" 不一定要寫出來,
#+ 因為無論如何腳本都會返回最后執行命令的退出狀態。
```
## 復合比較
### -a
邏輯與
`exp1 -a exp2` 返回真當且僅當 `exp1` 和 `exp2` 均為真。
### -o
邏輯或
如果 `exp1` 或 `exp2` 為真,則 `exp1 -o exp2` 返回真。
以上兩個操作和 [雙方括號](http://tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETS) 結構中的 Bash 比較操作符號 `&&` 和 `||` 類似。
```bash
[[ condition1 && condition2 ]]
```
測試操作 `-o` 和 `-a` 可以在 [`test`](http://tldp.org/LDP/abs/html/testconstructs.html#TTESTREF) 命令或在測試括號中進行。
```bash
if [ "$expr1" -a "$expr2" ]
then
echo "Both expr1 and expr2 are true."
else
echo "Either expr1 or expr2 is false."
fi
```
 rihad 指出:
```bash
[ 1 -eq 1 ] && [ -n "`echo true 1>&2`" ] # 真
[ 1 -eq 2 ] && [ -n "`echo true 1>&2`" ] # 沒有輸出
# ^^^^^^^ 條件為假。到這里為止,一切都按預期執行。
# 但是
[ 1 -eq 2 -a -n "`echo true 1>&2`" ] # 真
# ^^^^^^^ 條件為假。但是為什么結果為真?
# 是因為括號內的兩個條件子句都執行了么?
[[ 1 -eq 2 && -n "`echo true 1>&2`" ]] # 沒有輸出
# 并不是。
# 所以顯然 && 和 || 具備“短路”機制,
#+ 例如對于 &&,若第一個表達式為假,則不執行第二個表達式直接返回假,
#+ 而 -a 和 -o 則不是。
```
復合比較操作的例子可以參考 [樣例 8-3](http://tldp.org/LDP/abs/html/ops.html#ANDOR),[樣例 27-17](http://tldp.org/LDP/abs/html/arrays.html#TWODIM) 和 [樣例 A-29](http://tldp.org/LDP/abs/html/contributed-scripts.html#WHX)。
[^1]: S.C. 指出在復合測試中,僅僅引用字符串可能還不夠。比如表達式 `[ -n "$string" -o "$a" = "$b" ]` 在某些 Bash 版本下,如果 `$string` 為空可能會出錯。更加安全的方式是,對于可能為空的字符串,添加一個額外的字符,例如 `[ "x$string" != x -o "x$a" = "x$b" ]`(其中的 x 互相抵消)。
- 第一部分 初見shell
- 1. 為什么使用shell編程
- 2. 和Sha-Bang(#!)一起出發
- 2.1 調用一個腳本
- 2.2 牛刀小試
- 第二部分 shell基礎
- 3. 特殊字符
- 4. 變量與參數
- 4.1 變量替換
- 4.2 變量賦值
- 4.3 Bash弱類型變量
- 4.4 特殊變量類型
- 5. 引用
- 5.1 引用變量
- 5.2 轉義
- 6. 退出與退出狀態
- 7. 測試
- 7.1 測試結構
- 7.2 文件測試操作
- 7.3 其他比較操作
- 7.4 嵌套 if/then 條件測試
- 7.5 牛刀小試
- 8. 運算符相關話題
- 8.1 運算符
- 8.2 數字常量
- 8.3 雙圓括號結構
- 8.4 運算符優先級
- 第三部分 shell進階
- 10. 變量處理
- 10.1 字符串處理
- 10.1.1 使用 awk 處理字符串
- 10.1.2 參考資料
- 10.2 參數替換
- 11. 循環與分支
- 11.1 循環
- 11.2 嵌套循環
- 11.3 循環控制
- 11.4 測試與分支
- 12. 命令替換
- 13. 算術擴展
- 14. 休息時間
- 第五部分 進階話題
- 19. 嵌入文檔
- 20. I/O 重定向
- 20.1 使用 exec
- 20.2 重定向代碼塊
- 20.3 應用程序
- 22. 限制模式的Shell
- 23. 進程替換
- 26. 列表結構
- 25. 別名