# 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" ]`

![caution](http://tldp.org/LDP/abs/images/caution.gif) 注意在`=`前后要加上[空格](http://tldp.org/LDP/abs/images/caution.gif)

`if [ "$a"="$b" ]` 和上面不等价。

### ==

等于

`if [ "$a" == "$b" ]`

和 `=` 同义

![note](http://tldp.org/LDP/abs/images/note.gif) `==` 运算符在 [双方括号](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`）。

![caution](http://tldp.org/LDP/abs/images/caution.gif) 使用 `-n` 时字符串必须是在括号中且被引用的。使用 `! -z` 判断未引用的字符串或者直接判断（[样例 7-6](http://tldp.org/LDP/abs/html/comparison-ops.html#STRTEST)）通常可行，但是非常危险。判断字符串时一定要引用。

样例 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
```

![caution](http://tldp.org/LDP/abs/images/caution.gif) 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)。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wiki.clay-wangzhi.com/shell/part2/07_tests/07_3_other_comparison_operators.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
