【发布时间】:2013-12-25 07:37:57
【问题描述】:
shell脚本中=、==和-eq有什么区别?
以下有什么区别吗?
[ $a = $b ]
[ $a == $b ]
[ $a -eq $b ]
难道只是=和==只在变量包含数字时才使用?
【问题讨论】:
shell脚本中=、==和-eq有什么区别?
以下有什么区别吗?
[ $a = $b ]
[ $a == $b ]
[ $a -eq $b ]
难道只是=和==只在变量包含数字时才使用?
【问题讨论】:
反之亦然:= 和 == 用于字符串比较,-eq 用于数字比较。 -eq 与 -lt、-le、-gt、-ge 和 -ne 属于同一家族,如果这有助于您记住哪个是哪个。
== 是一种 bash 主义,顺便说一下。最好使用 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 脚本,那么我推荐using [[ instead。双重形式具有更多功能、更自然的语法和更少的陷阱。 $a 周围不再需要双引号,对于一个:
$ [[ $a == foo ]]; echo "$?" # bash specific
0
另见:
【讨论】:
这取决于运算符周围的Test Construct。您可以选择双括号、双括号、单括号或test。
如果您使用((…)),您正在测试与== 的算术相等性,就像在C 中一样:
$ (( 1==1 )); echo $?
0
$ (( 1==2 )); echo $?
1
(注意:0 表示 Unix 意义上的 true,失败的测试结果为非零数字。)
在双括号内使用-eq 是语法错误。
如果您使用[…](或单括号)或[[…]](或双括号)或test,您可以使用-eq、-ne、-lt、-le之一, -gt,或-ge 作为arithmetic comparison。
$ [ 1 -eq 1 ]; echo $?
0
$ [ 1 -eq 2 ]; echo $?
1
$ test 1 -eq 1; echo $?
0
单括号或双括号(或test 命令)内的== 是string comparison operators 之一:
$ [[ "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。很多人都被这个bug咬了:
$ [[ 10 < 2 ]]; echo $?
0
10 在算术上小于2 的正确测试是这样的:
$ [[ 10 -lt 2 ]]; echo $?
1
在 cmets 中,有一个关于 技术 的问题,为什么在字符串上使用整数 -eq 对于不相同的字符串返回 true:
$ [[ "yes" -eq "no" ]]; echo $?
0
原因是 Bash 是untyped。 -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一般不支持[[...]]和((...))或者==。
【讨论】:
[[ "yes" -eq "no" ]] 返回 True 的技术原因。 bash 如何将这些字符串强制转换为可以比较的整数值? ;-)
[[ "yes" -eq "no" ]] 等价于[[ "yes" -eq 0 ]] 或[[ "yes" -eq "any_noninteger_string" ]] -- 全部为真。 -eq 强制进行整数比较。 "yes" 被解释为整数0;如果另一个整数是 0 或字符串结果是 0,则比较结果为 True。
==,而在下方仅提及(便携式,标准化)=。
== 是 = 的 bash 特定别名,它执行字符串(词法)比较而不是数字比较。 eq 当然是数字比较。
最后,我通常更喜欢使用if [ "$a" == "$b" ]的形式
【讨论】:
== 是错误的形式,因为只有= 是由POSIX 指定的。
==,那么把它放在[[和]]之间。 (并确保脚本的第一行指定使用/bin/bash。)
几个答案显示了危险的例子。 OP 的示例 [ $a == $b ] 专门使用了不带引号的变量替换(截至 2017 年 10 月的编辑)。对于[...],字符串相等是安全的。
但是,如果您要列举像[[...]] 这样的替代项,您还必须告知右侧必须被引用。如果没有引用,那就是模式匹配! (来自 Bash 手册页:“可以引用模式的任何部分以强制将其作为字符串匹配。”)。
在 Bash 中,产生“是”的两个语句是模式匹配,其他三个是字符串相等:
$ 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
$
【讨论】:
[ "$lht" = "$rht" ] 带引号才能可靠,即使相等。如果您有一个使用touch 'Afoo -o AB' 创建的文件,[ $lft = $rht ] 将返回 true,即使该文件名根本不与 AB 相同。