在shell脚本中=,==和-eq之间的区别是什么?

下面这些有什么区别吗?

[ $a = $b ]
[ $a == $b ]
[ $a -eq $b ]

仅仅是=和==只在变量中包含数字时使用吗?


当前回答

有几个答案给出了危险的例子。OP的示例[$a == $b]特别使用了未加引号的变量替换(截至2017年10月编辑)。对[…对于字符串相等是安全的。

但如果你要列举一些替代选项,比如[[…]],你还必须通知右边必须引用。如果没有引用,则是模式匹配!(来自Bash手册页:“模式的任何部分都可以被引用,以强制将其作为字符串进行匹配。”)

在Bash中,产生“yes”的两个语句是模式匹配,其他三个是字符串相等:

$ rht="A*"
$ lft="AB"
$ [ $lft = $rht ] && echo yes
$ [ $lft == $rht ] && echo yes
$ [[ $lft = $rht ]] && echo yes
yes
$ [[ $lft == $rht ]] && echo yes
yes
$ [[ $lft == "$rht" ]] && echo yes
$

其他回答

它取决于操作符周围的Test Construct。您可以选择双括号、双括号、单括号或test。

如果你使用((…)),你是在用==测试算术相等,就像在C中一样:

$ (( 1==1 )); echo $?
0
$ (( 1==2 )); echo $?
1

(注意:0在Unix意义上表示为真,失败的测试结果是非零数字。)

在双括号内使用-eq是一个语法错误。

如果您使用[…](或单括号)或[[…]](或双括号)或test,您可以使用-eq, -ne, -lt, -le, -gt或-ge中的一个作为算术比较。

$ [ 1 -eq 1 ]; echo $?
0
$ [ 1 -eq 2 ]; echo $?
1
$ test 1 -eq 1; echo $?
0

单括号或双括号(或test命令)内的==是字符串比较操作符之一:

$ [[ "abc" == "abc" ]]; echo $?
0
$ [[ "abc" == "ABC" ]]; echo $?
1

作为字符串操作符,=等价于==。另外,请注意=或==周围的空白:这是必需的。

当您可以执行[[1 == 1]]或[[$((1+1))== 2]]时,它正在测试字符串相等-而不是算术相等。

因此-eq产生的结果可能是1+1的整数值等于2,即使右边是一个字符串,并且有一个尾随空格:

$ [[ $(( 1+1 )) -eq  "2 " ]]; echo $?
0

而相同的字符串比较会选择尾随空格,因此字符串比较失败:

$ [[ $(( 1+1 )) == "2 " ]]; echo $?
1

错误的字符串比较可能会产生完全错误的答案。10在字典上小于2,因此字符串比较返回true或0。很多人都被这个问题所困扰:

$ [[ 10 < 2 ]]; echo $?
0

10算术上小于2的正确测试是这样的:

$ [[ 10 -lt 2 ]]; echo $?
1

在评论中,有一个关于为什么对字符串使用整数-eq对于不相同的字符串返回true的技术原因的问题:

$ [[ "yes" -eq "no" ]]; echo $?
0

原因是Bash是无类型的。-eq使字符串被解释为整数(如果可能的话,包括进制转换):

$ [[ "0x10" -eq 16 ]]; echo $?
0
$ [[ "010" -eq 8 ]]; echo $?
0
$ [[ "100" -eq 100 ]]; echo $?
0

如果Bash认为它只是一个字符串,则为0:

$ [[ "yes" -eq 0 ]]; echo $?
0
$ [[ "yes" -eq 1 ]]; echo $?
1

所以[["yes" -eq "no"]]等于[[0 -eq 0]]]


最后注意:许多Bash特定于测试构造的扩展都不是POSIX,因此在其他shell中可能会失败。其他shell通常不支持[[…]]]和((…))或==。

有几个答案给出了危险的例子。OP的示例[$a == $b]特别使用了未加引号的变量替换(截至2017年10月编辑)。对[…对于字符串相等是安全的。

但如果你要列举一些替代选项,比如[[…]],你还必须通知右边必须引用。如果没有引用,则是模式匹配!(来自Bash手册页:“模式的任何部分都可以被引用,以强制将其作为字符串进行匹配。”)

在Bash中,产生“yes”的两个语句是模式匹配,其他三个是字符串相等:

$ rht="A*"
$ lft="AB"
$ [ $lft = $rht ] && echo yes
$ [ $lft == $rht ] && echo yes
$ [[ $lft = $rht ]] && echo yes
yes
$ [[ $lft == $rht ]] && echo yes
yes
$ [[ $lft == "$rht" ]] && echo yes
$

=和==用于字符串比较 -eq用于数值比较 -eq与-lt, -le, -gt, -ge和-ne属于同一个家族

==是bash专用的(在sh (Bourne shell)中不存在,…)出于兼容性考虑,建议使用POSIX =。在bash中,这两个是等价的,在sh =中是唯一可以工作的。

$ a=foo
$ [ "$a" = foo ]; echo "$?"       # POSIX sh
0
$ [ "$a" == foo ]; echo "$?"      # bash-specific
0
$ [ "$a" -eq foo ]; echo "$?"     # wrong
-bash: [: foo: integer expression expected
2

(注意:确保引用变量展开。不要漏掉上面的双引号。)

如果你在写#!/bin/bash脚本,那么我建议使用[[代替。双方括号[[…]]形式有更多的特性,更自然的语法,更少的陷阱会绊倒你。例如,在$a附近不再需要双引号:

$ [[ $a == foo ]]; echo "$?"      # bash-specific
0

参见:

Bash中的[和[]有什么区别?

==是=的bash特定别名,它执行字符串(词法)比较,而不是数值比较。Eq当然是数值比较。

最后,我通常喜欢使用if ["$a" == "$b"]这种形式