【问题标题】:Do we still need to use `[...]` in Bash?我们还需要在 Bash 中使用 `[...]` 吗?
【发布时间】:2015-08-31 21:23:14
【问题描述】:

如果我不关心兼容性或可移植性问题,我是否还需要在 Bash 中使用测试构造 [...]

许多教程/书籍告诉[[...]][...] 更好、更灵活,例如[[...]] 支持 glob/regex,但今天我仍然看到很多人更喜欢使用 [...] 作为标准形式。

这背后的原因是什么?什么[...]可以做而[[...]]不能做?

【问题讨论】:

  • [...] 可能会导致错误,如果您不使用正确的空格并正确引用参数扩展,[[ ... ]] 不会。否则,它仅用于保持 shell 脚本的可移植性。
  • [ 是(更像)一个普通命令,与任何其他命令具有相同的参数解释。 [[ 命令对参数的解释与其他命令不同。如果您不介意围绕处理参数的不同方式绞尽脑汁,请使用[[。你会相处得很好——见证前面的评论。但是[ 使用与任何其他命令相同的符号,因此它比[[ 更易于使用。
  • 当我开始学习 Bash 时,几种形式的测试构造让我很困扰,我总是使用 Bash 作为我项目的胶水代码,我只需要一个不关心可移植性的最佳解决方案/兼容问题。顺便说一句,关于[..] 的任何重要说明请在下面回答我。
  • 我看到人们使用[ 犯的大多数错误让我认为他们正在对其应用第三组规则,这些规则不适用于[[ 或普通命令。如果有的话,如果你打算使用它,我建议只使用名称test
  • 当可移植性不是问题时,普遍接受的智慧是“使用[[”。可移植性对我来说是个问题,而且我从事 shell 编程已经有很长时间了——我更喜欢[,因为它对我来说使用起来更简单,因为它“只是一个没有特殊情况的常规命令”(这也不是确实如此,但特殊情况并没有给我带来问题,因为我在 30 年前将这些问题内化了)。你的选择,但如果你真的确定便携性不是问题,那么可能会选择[[

标签: bash if-statement conditional-statements


【解决方案1】:

我发现 一个[ 实现但 [[ 没有实现的功能。但是,无论如何,这不是你应该使用的东西,所以我不会把它称为使用 [ 而不是 [[ 的理由。

test 支持用于布尔 AND 和 OR 的 -a-o 运算符:

if [ 3 -eq 3 -a 4 -eq 4 ]; then
    echo true
fi

但是,尝试将它们与[[ 一起使用会出现语法错误:

# The correct version is
# if [[ 3 -eq 3 && 4 -eq 4 ]]; then echo yay; fi
$ if [[ 3 -eq 3 -a 4 -eq 4 ]]; then echo yay; fi
bash: syntax error in conditional expression
bash: syntax error near `-a'

但如果您使用的是[[,您将使用&&||,即使支持-a-o。此外,即使是 POSIX 标准也建议使用[ ... ] && [ ... ] 代替-a(以及[ ... ] || [ ... ] 而不是-o);这两个运算符是符合 POSIX 实现不需要的扩展。


除了可移植性问题外,其他原因只是见仁见智。

【讨论】:

  • 我不会说[[ 没有实现-a-o 的功能;我想说他们在那里拼写为&&||
  • 我同意查尔斯·达菲的观点。但是,更一般地说,[[ 的大部分优点都可以被视为功能缺陷。例如,考虑foo='3 -lt 4' ; if [ $foo ] ; then ...
  • 很公平,但确实不能简单地将 100% 的有效[ ... ] 命令“包装”在另一层方括号中并让它工作。 (我最初的回答是,[ 不支持[[ 所不支持的没有,直到我偶然发现了这个小花絮,ruakh 的有效观点除外。)
【解决方案2】:

除了已经讨论过的[ 的可移植性优势和[[ 的新功能和安全/正确性属性之外,使用[[ 的一个缺点是值得注意的(在我看来) .

具体来说,当您使用它来验证/等时会发生什么。数值。

我问了一个问题here

我还在 this answer 的 cmets 中讨论过。

具体来说,这个

foo=bar
[ 5 -eq "$foo" ]

将输出错误,[ 将返回 2,而 this(变量周围有或没有引号)

foo=bar
[[ 5 -eq "$foo" ]]

会默默地返回1 和这个

bar=5
foo=bar
[[ 5 -eq "$foo" ]]

将返回0

[[ 递归地评估裸变量。正如 ruakh 对 chepner 答案的评论所指出的,它还将扩展变量中的表达式。

所以

foo="10 / 2"
[[ 5 -eq "$foo" ]]

也将返回 true。

现在,这可能正是您想要的,但这意味着您必须更加努力地验证输入/等。然后你会用[

【讨论】:

  • 有趣。 POSIX 似乎没有指定这种递归查找,尽管它也没有禁止它。在dash 中,echo $(( foo )) 是一个错误,但echo $(( $foo )) 有效。第三级间接是正确的。 baz=fooecho $(( baz ))echo $(( $baz )) 都不起作用。
  • @chepner echo $(( foo ))$foo 包含一个数字值是POSIX 指定工作(并且似乎在dash 中,dash -c 'echo $(( foo ))' 按预期返回0。@987654347另一方面,@ 是与dash -c 'echo $(( ))' 相同的错误),是的,当$foo 包含未指定的数值时会发生什么,这就是bash 的行为。是的,dash 具有对我来说更有意义的单级扩展(尽管错误表明它扩展了变量并然后炸毁,而不是不扩展和炸毁)。
猜你喜欢
  • 1970-01-01
  • 2020-08-22
  • 1970-01-01
  • 2015-10-24
  • 2019-08-26
  • 2022-07-15
  • 2021-10-24
  • 1970-01-01
  • 2019-03-04
相关资源
最近更新 更多