问题 bash如果有或否定


为什么:

#!/bin/bash
wtf=false
if [ $wtf ] || [ ! -f filethatexists.whatever ]
then
 echo "WTF1"
fi
if [ ! -f filethatexists.whatever ]
then
 echo "WTF2"
fi

打印:

WTF1

而不是什么?特别令人困惑的是,第二种形式按预期工作,而第一种形式没有。


10886
2017-09-26 22:53


起源



答案:


基本测试

[ $wtf ]

测试中间的字符串是否为空。

以来 $wtf 包含字符串 'false',测试返回true,或退出状态0表示成功,因为 'false' 与空字符串不同 ''  - 因此你得到了 WTF1 作为回应。

试试:

wtf=''

正如所指出的那样 戈登戴维森 (和 丹尼斯威廉姆森),小心你正在测试的字符串是个好主意。的确,我应该说我会一直使用 [ -n "$wtf" ] 要么 [ -z "$wtf" ] 测试是否设置了一个变量,因为在我学习shell的时候,这是四分之一世纪以前的必要条件。我有来自Bash afficionados的反故事,你不必在Bash中担心它 - 但是,我认为这里的代码提供了一个反例,事实上你仍然需要担心它。

所以,一些最佳实践:

  • 用双引号括起测试变量,或
  • (在Bash中),使用 [[ $wtf ]] 它知道如何处理变量扩展。
  • 使用 -n 要么 -z 测试以测试非空值或空值。

规则可以有例外 - 但是跟随它们你不会走得太远。

考虑一下代码:

wtf="1 -eq 0"
 [  $wtf  ]  && echo "WTF0"
[[  $wtf  ]] && echo "WTF1"
wtf="false"
 [  $wtf  ]  && echo "WTF2"
[[  $wtf  ]] && echo "WTF3"
wtf=""
 [  $wtf  ]  && echo "WTF4"
[[  $wtf  ]] && echo "WTF5"
wtf="false"
 [ "$wtf" ]  && echo "WTF6"
[[ "$wtf" ]] && echo "WTF7"
wtf=""
 [ "$wtf" ]  && echo "WTF8"
[[ "$wtf" ]] && echo "WTF9"

这会产生:

WTF1
WTF2
WTF3
WTF6
WTF7

同时使用bash和ksh(在MacOS X 10.6.4上找到,使用'bash testcode.sh'或'ksh testcode.sh'运行时)。一个真正的Bourne shell(如果你仍然可以找到这样的东西)会反对双括号操作 - 它将无法找到命令'[[' 上 $PATH

您可以扩展测试以覆盖更多令人作呕的案例。


10
2017-09-26 22:55



好的,有道理。语法高亮使我认为布尔是一个给定的东西。我猜想我太过分了。 - i30817
实际上,它甚至比那更奇怪 - 如果$ wtf中有任何空格,它将被评估为表达式。尝试一下 wtf="1 -eq 0" 和 wtf="1 -eq 1 看效果。对于更喜剧,请尝试 wtf="1 -eq" - Gordon Davisson
测试4/5是测试8/9的复制品。我认为你的意思是在变量周围没有引号的情况下做4/5(但结果是相同的 - 没有回应)。此外,通过引用变量而改变的测试0/1不会评估 1 -eq 0 所以两种括号类型都评估为真(如测试2/3)。有趣的是,在Bash和ksh中, [ '' ], [[ '' ]] 和 [ ] 然而,每个工作,并返回false [[ ]] 产生错误。 - Dennis Williamson


答案:


基本测试

[ $wtf ]

测试中间的字符串是否为空。

以来 $wtf 包含字符串 'false',测试返回true,或退出状态0表示成功,因为 'false' 与空字符串不同 ''  - 因此你得到了 WTF1 作为回应。

试试:

wtf=''

正如所指出的那样 戈登戴维森 (和 丹尼斯威廉姆森),小心你正在测试的字符串是个好主意。的确,我应该说我会一直使用 [ -n "$wtf" ] 要么 [ -z "$wtf" ] 测试是否设置了一个变量,因为在我学习shell的时候,这是四分之一世纪以前的必要条件。我有来自Bash afficionados的反故事,你不必在Bash中担心它 - 但是,我认为这里的代码提供了一个反例,事实上你仍然需要担心它。

所以,一些最佳实践:

  • 用双引号括起测试变量,或
  • (在Bash中),使用 [[ $wtf ]] 它知道如何处理变量扩展。
  • 使用 -n 要么 -z 测试以测试非空值或空值。

规则可以有例外 - 但是跟随它们你不会走得太远。

考虑一下代码:

wtf="1 -eq 0"
 [  $wtf  ]  && echo "WTF0"
[[  $wtf  ]] && echo "WTF1"
wtf="false"
 [  $wtf  ]  && echo "WTF2"
[[  $wtf  ]] && echo "WTF3"
wtf=""
 [  $wtf  ]  && echo "WTF4"
[[  $wtf  ]] && echo "WTF5"
wtf="false"
 [ "$wtf" ]  && echo "WTF6"
[[ "$wtf" ]] && echo "WTF7"
wtf=""
 [ "$wtf" ]  && echo "WTF8"
[[ "$wtf" ]] && echo "WTF9"

这会产生:

WTF1
WTF2
WTF3
WTF6
WTF7

同时使用bash和ksh(在MacOS X 10.6.4上找到,使用'bash testcode.sh'或'ksh testcode.sh'运行时)。一个真正的Bourne shell(如果你仍然可以找到这样的东西)会反对双括号操作 - 它将无法找到命令'[[' 上 $PATH

您可以扩展测试以覆盖更多令人作呕的案例。


10
2017-09-26 22:55



好的,有道理。语法高亮使我认为布尔是一个给定的东西。我猜想我太过分了。 - i30817
实际上,它甚至比那更奇怪 - 如果$ wtf中有任何空格,它将被评估为表达式。尝试一下 wtf="1 -eq 0" 和 wtf="1 -eq 1 看效果。对于更喜剧,请尝试 wtf="1 -eq" - Gordon Davisson
测试4/5是测试8/9的复制品。我认为你的意思是在变量周围没有引号的情况下做4/5(但结果是相同的 - 没有回应)。此外,通过引用变量而改变的测试0/1不会评估 1 -eq 0 所以两种括号类型都评估为真(如测试2/3)。有趣的是,在Bash和ksh中, [ '' ], [[ '' ]] 和 [ ] 然而,每个工作,并返回false [[ ]] 产生错误。 - Dennis Williamson


这是一个方便的小技巧:

wtf=false
if $wtf || [ ! -f filethatexists.whatever ]

在这种形式中,执行变量的内容,返回值确定测试是通过还是失败。它发生了 true 和 false 是Bash builtins,返回适当的值。


4
2017-09-26 23:40



这个“方便的小技巧”几乎不是好习惯。如果你有一个代码路径在哪里 wtf 包含除以外的任何内容 true 要么 false,令人惊讶的行为结果。空字符串?先前的价值 $?继续! rm -rf /?呃... - Charles Duffy


if [ $wtf = true ] || [ ! -f . .


0
2017-09-26 22:58



甚至“if [[$ wtf == true]] || [!-f ...” - dimba
@dimba这是一种讽刺,但是你不能在bash或bash兼容的shell之外运行它。 - Justin C. B.