[[expr1 ||之间的差异 expr2]]和[[expr1]] || [[expr2]]


7

考虑两个条件表达式expr1expr2,例如$i -eq $j$k -eq $l。我们可以用bash多种方式编写。这有两种可能性

[[ expr1 || expr2 ]]

[[ expr1 ]] || [[ expr2 ]]

我相当确定我在这里看到了推荐使用第二种方法的建议,但是我找不到证据来支持这一点。

这是一个示例脚本,似乎证明没有区别:

for i in 0 1
do
  for j in 0 1
  do
    for k in 0 1
    do
      for l in 0 1
      do
        if [[ $i -eq $j || $k -eq $l ]]; then printf "1-yes\t"; else printf "1-no\t"; fi
        if [[ $i -eq $j ]] || [[ $k -eq $l ]]; then printf "2-yes\n"; else printf "2-no\n"; fi
      done
    done
  done
done

输出显示两个条件构造产生相同的结果:

1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-no    2-no
1-no    2-no
1-yes   2-yes
1-yes   2-yes
1-no    2-no
1-no    2-no
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes

使用一个构造比另一个构造有什么好处吗?

对于加分,相同的问题,但使用||和推广到多个条件&&。例如,[[ expr1 && expr2 || expr3 ]]


关于为什么经常给出这类建议。[test不是 [[),OBPOSIX规范中test搜索标记(链接到“过时的”定义)。在test,有一些病理情况下,就不可能说,如果(还是)意在语法有意义的测试命令或字符串进行测试,那么,谁忽视了过时的标记和使用的语法人们实际需要的otherwise-过时的"x$foo"做法;使用[[,这不是问题。
查尔斯·达菲,

Answers:


8

我认为您看到的建议是针对POSIX sh和/或作为test命令加倍的[命令,而不是[[出现在ksh中的结构(感谢StéphaneChazelas的技巧),并且还用于bash,zsh和其他一些结构中贝壳。

在大多数语言(例如C)中,当已知一个子句为真或假时,就不必根据运算来评估其余部分:如果为true,则不在逻辑后面后面;如果为false,则不在逻辑之后,当然,例如,这允许在指针为NULL时停止并且不尝试在下一个子句中取消引用它。

但是sh[ expr1 -o expr2 ]构造(包括bash的实现)不能做到这一点:当人们只希望对expr1进行评估时,它总是对双方进行评估。这样做可能是为了与test命令实现兼容。另一方面,sh ||&&do遵循通常的原则:如果不改变结果,则不进行评估。

因此,需要注意的区别是:

: > /tmp/effect #clear effect
if [ 1 -eq 1 -o $(echo 1; echo or-was-evaluated > /tmp/effect) -eq 1 ]; then
    echo true;
fi
cat /tmp/effect

产生:

true
or-was-evaluated

上面,每个命令[都可以被替换,/usr/bin/[它是test命令的别名,之前[已内置到shell中使用。

接下来的两个构造:

: > /tmp/effect #clear effect
if [ 1 -eq 1 ] || [ $(echo 1; echo or-was-evaluated > /tmp/effect) -eq 1 ]; then
    echo true;
fi
cat /tmp/effect

要么

: > /tmp/effect #clear effect
if [[ 1 -eq 1 || $(echo 1; echo or-was-evaluated > /tmp/effect) -eq 1 ]]; then
    echo true;
fi
cat /tmp/effect

将只产生true并保留为effect空:||行为正确,并且也[[纠正了此问题。


更新:

正如@StéphaneChazelas所评论的那样,我错过了与初始问题相关的几个差异。我只在这里(至少对我来说)说最重要的一个:运算符的优先级。

尽管外壳程序不考虑优先级:

if true || true && false; then
    echo true
else
    echo false
fi

产生(因为没有优先级,因此先true || true求值,然后求值&& false):

false

运算符内部[[ ]]&&优先级高于||

if [[ 1 -eq 1 || 1 -eq 1 && 1 -eq 0 ]]; then
    echo true
else
    echo false
fi

收益(由于1 -eq 1 && 1 -eq 0已分组,因此是的第二个成员||):

true

至少对于kshbashzsh

因此[[ ]][ ]与直接Shell逻辑运算符相比,行为得到了改善。


1
[ x -o y ]不必评估双方。虽然GNU test[中内置bash这样做,你会发现用strace zsh -c '[ x -o -f /x ]'[并不试图统计() /x。与相同mksh。但是的确如此,-o并且-a被严重破坏,不能可靠地使用并且被POSIX弃用。
斯特凡Chazelas

1
一个区别是,内部[[...]]&&具有比更高的优先级||,而外部则具有相同的优先级。比较ksh -c '[[ x || x && "" ]]'VSsh -c 'true || true && false'
斯特凡Chazelas

1
请注意,标准[/ test实用程序没有==运算符。等于运算符是=(注意这里面的KSH [[...]]=/ ==是不是平等的经营者,但模式匹配的)。
斯特凡Chazelas

1
相反。&&优先于||内部[[...]](或((...))),但优先于&&||外壳运算符。[[ x || y && z ]]is x || (y && z)while x || y && zis (x || y) && z(操作员从左到右评估,没有优先级)。类似如何*在具有优先权+1 + 2 * 31 + (2 * 3)),但+-具有相同的优先级。
斯特凡Chazelas

1
如果&&优先于||,则应为true || (true && false),而不是(true || true) && false
Prvt_Yadav '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.