【问题标题】:Why is [[ ! ! expr ]] equivalent to [[ ! expr ]] in bash?为什么是 [[ ! ! expr ]] 等价于 [[ ! expr ]] 在 bash 中?
【发布时间】:2021-05-18 14:34:38
【问题描述】:

在 bash 中,我不明白为什么第三条命令不正确:

[[ 1 -eq 1 ]]         # $? is 0
[[ ! 1 -eq 1 ]]       # $? is 1
[[ ! ! 1 -eq 1 ]]     # $? is 1 (??)
[[ ! ( ! 1 -eq 1 ) ]] # $? is 0

如果我将1 -eq 1 替换为任何真表达式,并用任何假表达式取反,似乎会做同样的事情。

【问题讨论】:

  • FWIW,[[ ! ! 1 == 1 ]]; echo $?zsh 中按预期工作。
  • "expected" -- POSIX test 标准没有设定这种期望。
  • 我的期望是:syntax error
  • @WilliamPursell, ...当然,语法错误将始终具有非成功退出状态,因此这与 OP 报告的 1 一致。 (它还符合[ 的 POSIX 指南,因为语法错误是人们在收到未指定行为的输入时可以合法做的一系列事情的成员。
  • @vimene, ...关于“结果未指定”的美妙之处之一是 anything 是允许的。正如 C 标记中的人们所说的那样,给定调用未指定行为的输入,编译器可能会发出吃掉你的猫的代码;任何事情都是允许的。

标签: bash shell command-line


【解决方案1】:

[[ 是一种扩展语法,它提供(大部分)[ 的超集。那么,要了解它的行为,应该从[ 的标准开始。

The POSIX test specification 描述了[ 的预期行为方式。在一个地方,它确实提供了符合这个问题中描述的期望的描述:

! expression - 如果表达式为假则为真。如果表达式为真,则为假。

...但稍后,关于如何根据原语数量进行解析的更详细描述与此预期相矛盾:

  • 0 个参数:退出 false (1)。
  • 1 参数:如果 $1 不为空,则退出 true (0);否则,退出 false。
  • 2 个参数:如果 $1 是 !,如果 $2 为空则退出 true,如果 $2 不为空则退出 false。 如果 $1 是一元主元,如果一元测试为真,则退出 true,如果一元测试为假,则退出 false。 否则,会产生未指定的结果。
  • 3 个参数:如果 $2 是二进制主节点,则执行 $1 和 $3 的二进制测试。 如果 $1 是 '!',则否定 $2 和 $3 的两个参数测试。 (过时的 XSI 行为:如果 $1 是 '(' 而 $3 是 ')',则执行 $2 的一元测试。 在不支持 XSI 选项的系统上,如果 $1 是 '(' 并且 $3 是 ')',则结果未指定。 否则,会产生未指定的结果。
  • 4 个参数:如果 $1 是 '!',则否定 $2、$3 和 $4 的三参数测试。 (过时的 XSI 行为:如果 $1 是 '(' 而 $4 是 ')',则执行 $2 和 $3 的两个参数测试。) 在不支持 XSI 选项的系统上,如果 $1 是 '(' 而 $4 是 ')',则结果未指定。 否则,结果未指定。
  • 超过 4 个参数:未指定结果。

! ! 1 -eq 1 的情况下,您有一个五个参数的情况。 结果未指定,因为如果第一个参数是!,标准没有指定五参数情况是四参数情况的否定。


正如 Zilog80 所建议的:如果您不想受到这些限制,请考虑将您的 ! 放在测试语法之外; ! [[ ... ]] 发生在 shell 命令解析层,而不是定制的特定于测试语法的逻辑中,! ! [[ ... ]] 在那里完全有效。

【讨论】:

  • AFAIK,正确的否定方法是 ! ! [[ 1 -eq 1 ]] ,如果值得一提的话。
  • @Zilog80,我同意;该方法受制于 POSIX sh 标准而不是 POSIX 测试标准,并且双重否定在那里有效。
  • 并非如此。标准文档[,而不是[[,因此,即使[[ string ]] 也未指定。
  • @oguzismail, ...当然,它不是严格 POSIX 指定的,但它是标准的扩展,因此该标准为它提供了一些目标(受记录在案的地方存在分歧)。这比其他方式更接近于规范参考。
  • @oguzismail, ...我想知道我们是否在互相交谈。我要说的只是行为是未指定的,这意味着外壳 没有义务 使该标志成为切换与设置并保持设置的东西;保持其当前实现与拥有一个基于切换的实现方式一样正确,其行为方式符合 OP 的预期。 (当然,未指定也意味着它可以在不破坏任何保证的情况下在新版本中切换到另一种方式......但是,它也可以切换回来)。
【解决方案2】:

在解析! 时,bash 为以下表达式设置一个标志,告诉它反转结果。正因为如此,解析两个或多个! 相当于解析一个!。我提出了一个patch 来解决这个问题,它被接受了。

【讨论】:

    猜你喜欢
    • 2011-10-26
    • 2020-12-18
    • 2018-11-04
    • 2019-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-23
    相关资源
    最近更新 更多