首页 > 解决方案 > 如果 -eq 与 [[ ... ]] 内的非数字参数一起使用,如何使 bash 抛出错误?

问题描述

我最近编写了一个相当复杂的 bash 脚本,在测试期间发现了一个不当行为。在分析过程中,我发现有一个错字,我不小心使用-eq了而不是==在条件命令中,即我有类似的东西

[[ "$var1" -eq "$var2" ]] || { ... }

代替

[[ "$var1" == "$var2" ]] || { ... }

由于在这种情况下var1并且var2可以包含任意字符串,而不仅仅是整数,所以在那个地方出现了可怕的错误。在命令行进行快速调查使问题更加清晰:

root@cerberus:~/scripts# [[ '0' -eq '0' ]] && echo Equal
Equal
root@cerberus:~/scripts# [[ '0' -eq '1' ]] && echo Equal
root@cerberus:~/scripts# [[ 'A' -eq 'B' ]] && echo Equal
Equal

最后一行让我非常惊讶。从bash 手册(在页面末尾,强调我的):

参数 1 操作参数 2

OP 是“-eq”、“-ne”、“-lt”、“-le”、“-gt”或“-ge”之一。如果 arg1 分别等于、不等于、小于、小于或等于、大于或大于或等于 arg2,则这些算术二元运算符返回 true。Arg1 和 arg2 可以是正整数或负整数。当与 [[ 命令一起使用时,Arg1 和 Arg2 被计算为算术表达式(请参阅 Shell Arithmetic)。

由于上面代码中的最后一行-eq[[命令一起使用,'A'并且'B'根据上面的引用被评估为算术表达式。令我惊讶的是,这并没有使 bash 抛出错误,因为显然没有合理的方法可以将'A'整数'B'转换为整数。

然后我试图找出 bash 在评估算术表达式时适用的规则。手册中的相应章节对某些边缘情况进行了陈述(例如,表达式的扩展为nullor unset),但其中没有关于一个表达式的扩展是无法进行算术评估的任意字符串的情况。[注意:我知道我们不应该使用仅链接引用,但是各个部分对于引用来说太长了。]

出于好奇,我试图找出类似'A'实际计算的算术值。事实证明这比预期的要困难,因为 bash 实际上拒绝对字符串进行“正常”算术扩展,即使这些字符串可以进行算术评估:

root@cerberus:~/scripts# echo $(( 0 ))
0
root@cerberus:~/scripts# echo $(( '0' ))
-bash: '0' : syntax error: operand expected (error token is "'0' ")
root@cerberus:~/scripts# echo $(( 'A' ))
-bash: 'A' : syntax error: operand expected (error token is "'A' ")

因为我想解决问题,而不是症状,所以我放弃了尝试找出 bash 在算术评估任意字符串时适用的规则,或者为什么它在算术扩展期间抛出错误,而不是在算术期间结合评估[[

相反,我现在想知道 bash 中是否有一个选项可以使它在必须对表达式进行算术计算的任何情况下抛出错误,但这是不可能的,因为该表达式不是算术。如果没有这样的选项,我想知道当这些运算符在内部使用时,至少对于 , 的参数的算术评估是否可行-eq-ne等等)[[ ]]

标签: bashconditional-statementsarithmetic-expressions

解决方案


如果你使用[ ... ]而不是[[ ... ]],我认为你会得到你想要的行为。给定以下功能:

check_equal() {
  if [ "$var1" -eq "$var2" ]; then
    echo equal
  else
    echo not equal
  fi
}

这些按预期工作:

$ var1=0 var2=0 check_equal
equal
$ var1=0 var2=1 check_equal
not equal

但是传递非数字参数会导致错误消息:

$ var1=A var2=0 check_equal
testnumbers.sh: line 2: [: A: integer expression expected
not equal

当然,这里的缺点是您使用的[ ... ]表达式在某些方面比仅 bash 的[[ ... ]]表达式更脆弱。


推荐阅读