201

我正在阅读 bash 示例,if但有些示例是用单方括号编写的:

if [ -f $param ]
then
  #...
fi

其他带双方括号的:

if [[ $? -ne 0 ]]
then
    start looking for errors in yourlog
fi

有什么不同?

4

7 回答 7

235

Single[]是符合 posix shell 的条件测试。

Double[[]]是标准的扩展,[]并且受到 bash 和其他 shell(例如 zsh、ksh)的支持。它们支持额外的操作(以及标准的 posix 操作)。例如:||而不是-o和正则表达式匹配=~。更完整的差异列表可以在bash 手册的条件结构部分中找到。

[]每当您希望脚本可跨 shell 移植时使用。[[]]如果您想要不支持[]且不需要可移植的条件表达式,请使用此选项。

于 2012-11-24T15:59:25.173 回答
117

行为差异

在 Bash 4.3.11 中测试:

  • POSIX 与 Bash 扩展:

  • 常规命令与魔法

    • [只是一个带有奇怪名称的常规命令。

      ]只是 的最后一个参数[

    Ubuntu 16.04 实际上有一个/usr/bin/[coreutils提供的可执行文件,但 bash 内置版本优先。

    Bash 解析命令的方式没有任何改变。

    特别是,<是重定向,&&||连接多个命令,( )生成子shell,除非转义\,并且单词扩展照常发生。

    • [[ X ]]是一个X可以神奇地解析的单一结构。<, &&,||()被特殊对待,分词规则不同。

      还有更多的区别,如==~

    在 Bashese:[是一个内置命令,[[是一个关键字:https ://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • &&||

    • [[ a = a && b = b ]]: 真实的,合乎逻辑
    • [ a = a && b = b ]: 语法错误,&&解析为 AND 命令分隔符cmd1 && cmd2
    • [ a = a ] && [ b = b ]: POSIX 可靠等价物
    • [ a = a -a b = b ]a: 几乎等价,但被 POSIX 弃用,因为它是疯狂的,并且对于or blike !or的某些值会失败,或者(将被解释为逻辑操作
  • (

    • [[ (a = a || a = b) && a = b ]]: 错误的。没有( ), 会是真的,因为[[ && ]]它的优先级高于[[ || ]]
    • [ ( a = a ) ]: 语法错误,()被解释为子shell
    • [ \( a = a -o a = b \) -a a = b ]: 等效,但是(), -a, 和-o被 POSIX 弃用。没有\( \)会是真的,因为-a优先级高于-o
    • { [ a = a ] || [ a = b ]; } && [ a = b ]不推荐使用的 POSIX 等效项。然而,在这种特殊情况下,我们可以写成:[ a = a ] || [ a = b ] && [ a = b ]因为||&&shell 运算符具有相同的优先级,不像[[ || ]]and[[ && ]]-o, -aand[
  • 扩展时的分词和文件名生成 (split+glob)

    • x='a b'; [[ $x = 'a b' ]]:是的,不需要引号
    • x='a b'; [ $x = 'a b' ]: 语法错误,展开为[ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: 如果当前目录中有多个文件,则语法错误。
    • x='a b'; [ "$x" = 'a b' ]: POSIX 等价物
  • =

    • [[ ab = a? ]]:是的,因为它会进行模式匹配* ? [很神奇)。不 glob 扩展到当前目录中的文件。
    • [ ab = a? ]: a?glob 扩展。所以可能是真或假,具体取决于当前目录中的文件。
    • [ ab = a\? ]: false,不是全局扩展
    • =and在and==中是相同的,但是是一个 Bash 扩展。[[[==
    • case ab in (a?) echo match; esac: POSIX 等价物
    • [[ ab =~ 'ab?' ]]: false,''在 Bash 3.2 及更高版本中失去魔力,并且未启用对 bash 3.1 的兼容性(如 with BASH_COMPAT=3.1
    • [[ ab? =~ 'ab?' ]]: 真的
  • =~

    • [[ ab =~ ab? ]]: true,POSIX扩展正则表达式匹配,?不全局扩展
    • [ a =~ a ]: 语法错误。没有 bash 等价物。
    • printf 'ab\n' | grep -Eq 'ab?': POSIX 等效项(仅单行数据)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': POSIX 等价物。

建议:一直使用[]

[[ ]]我见过的每个构造都有 POSIX 等价物。

如果你使用[[ ]]你:

  • 失去便携性
  • 迫使读者了解另一个 bash 扩展的复杂性。[只是一个名称怪异的常规命令,不涉及特殊语义。

感谢Stéphane Chazelas的重要更正和补充。

于 2015-07-12T10:21:22.610 回答
17

=在用于条件测试的单括号内(即 [ ... ]),所有 shell 都支持诸如 single 的一些运算符,而==一些较旧的 shell 不支持使用运算符。

在条件测试的双括号内(即 [[ ... ]]),在旧 shell 或新 shell 中使用=or没有区别。==

编辑:我还应该注意:在 bash 中,如果可能,请始终使用双括号 [[ ... ]],因为它比单括号更安全。我将通过以下示例说明原因:

if [ $var == "hello" ]; then

如果 $var 恰好为 null / 空,​​那么这就是脚本所看到的:

if [ == "hello" ]; then

这会破坏你的脚本。解决方案是使用双括号,或者始终记住在变量周围加上引号 ( "$var")。双括号是更好的防御性编码实践。

于 2012-11-24T15:59:11.710 回答
14

[[是一个与命令类似(但比[命令更强大)的 bash 关键字。

http://mywiki.wooledge.org/BashFAQ/031http://mywiki.wooledge.org/BashGuide/TestsAndConditionals

除非您正在编写 POSIX sh,否则我们建议使用[[.

于 2012-11-24T16:17:18.757 回答
3
  • [类似于builtinprintf。Bash 语法期望在与命令相同的位置看到它。除了内置]函数期望它之外,这对 Bash 没有任何意义。[(man bash / SHELL BUILTIN 命令)
  • [[是一个keyword如果。Bash 语法开始也期望它在与命令相同的位置,但不是执行它,而是进入条件上下文。并且]]也是结束此上下文的关键字。(man bash / SHELL GRAMMAR / 复合命令)

按顺序,bash 尝试解析:语法关键字 > 用户别名 > 内置函数 > 用户函数 > $PATH 中的命令

type [  # [ is a shell builtin
type [[  # [[ is a shell keyword
type ]  # bash: type: ]: not found
type ]]  # ]] is a shell keyword
compgen -k  # Keywords: if then else ...
compgen -b  # Builtins: . : [ alias bg bind ...
which [  # /usr/bin/[
  • [较慢 <= 我猜它执行更多的解析代码。但我知道它调用了相同数量的系统调用(经过测试
  • [[即使对于人类来说,它在语法上也更容易解析,因为它开始了一个上下文。对于算术条件,考虑使用((.
time for i in {1..1000000}; do [ 'a' = 'b' ] ; done  # 1.990s
time for i in {1..1000000}; do [[ 'a' == 'b' ]] ; done  # 1.371s

time for i in {1..1000000}; do if [ 'a' = 'a' ]; then if [ 'a' = 'b' ];then :; fi; fi ; done  # 3.512s
time for i in {1..1000000}; do if [[ 'a' == 'a' ]]; then if [[ 'a' == 'b' ]];then :; fi; fi; done  # 2.143s

strace -cf  bash -c "for i in {1..100000}; do if [ 'a' = 'a' ]; then if [ 'a' = 'b' ];then :; fi; fi  ; done;"  # 399
strace -cf  bash -c "for i in {1..100000}; do if [[ 'a' == 'a' ]]; then if [[ 'a' == 'b' ]];then :; fi; fi  ; done;"  # 399

我建议使用[[:如果您不明确关心 posix 兼容性,则意味着您不是,所以不要关心获得“更多”兼容​​的脚本不是。

于 2021-06-26T22:08:04.820 回答
1

您可以使用方括号进行轻度正则表达式匹配,例如:

if [[ $1 =~ "foo.*bar" ]] ; then

(只要您使用的 bash 版本支持此语法)

于 2012-11-24T15:58:48.293 回答
1

Bash手册说:

当与 [[ 一起使用时,'<' 和 '>' 运算符使用当前语言环境按字典顺序排序。测试命令使用 ASCII 排序。

(测试命令等同于 [ ] )

于 2015-10-28T23:43:40.947 回答