【问题标题】:Shell equality operators (=, ==, -eq)Shell 等式运算符(=、==、-eq)
【发布时间】:2013-12-25 07:37:57
【问题描述】:

shell脚本中===-eq有什么区别?

以下有什么区别吗?

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

难道只是===只在变量包含数字时才使用?

【问题讨论】:

    标签: bash shell


    【解决方案1】:

    反之亦然:=== 用于字符串比较,-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
    

    另见:

    【讨论】:

      【解决方案2】:

      这取决于运算符周围的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,因此字符串比较返回true0。很多人都被这个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 如何将这些字符串强制转换为可以比较的整数值? ;-)
      • Bash 变量是untyped 所以[[ "yes" -eq "no" ]] 等价于[[ "yes" -eq 0 ]][[ "yes" -eq "any_noninteger_string" ]] -- 全部为真。 -eq 强制进行整数比较。 "yes" 被解释为整数0;如果另一个整数是 0 或字符串结果是 0,则比较结果为 True。
      • 嘘,嘶嘶声:在代码示例中显示(非便携式)==,而在下方仅提及(便携式,标准化)=
      【解决方案3】:

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

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

      【讨论】:

      • 在这里使用== 是错误的形式,因为只有= 是由POSIX 指定的。
      • 如果你真的坚持使用==,那么把它放在[[]]之间。 (并确保脚本的第一行指定使用/bin/bash。)
      【解决方案4】:

      几个答案显示了危险的例子。 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 相同。
      猜你喜欢
      • 2023-01-24
      • 2010-11-05
      • 2015-03-30
      • 1970-01-01
      • 2015-01-05
      • 1970-01-01
      • 1970-01-01
      • 2010-11-28
      • 2012-11-02
      相关资源
      最近更新 更多