800

一位同事最近在代码审查中声称,该[[ ]]构造[ ]在构造中比

if [ "`id -nu`" = "$someuser" ] ; then
     echo "I love you madly, $someuser"
fi

他无法提供理由。有吗?

4

9 回答 9

792

[[惊喜较少,通常使用起来更安全。但它不是可移植的——POSIX 没有指定它的作用,只有一些 shell 支持它(除了 bash,我听说 ksh 也支持它)。例如,你可以做

[[ -e $b ]]

测试文件是否存在。但是对于[,您必须引用$b,因为它会拆分论点并扩展诸如"a*"[[字面意思是)之类的内容。这也与如何[成为外部程序并像其他所有程序一样正常接收其参数有关(尽管它也可以是内置程序,但它仍然没有这种特殊处理)。

[[还有一些其他不错的功能,例如正则表达式匹配=~以及类似 C 语言中已知的运算符。这是一个很好的页面:测试和测试有什么区别?[[[Bash 测试

于 2009-03-21T15:44:09.887 回答
320

行为差异

Bash 4.3.11 的一些区别:

  • POSIX 与 Bash 扩展:

  • 常规命令与魔法

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

      ]只是 的最后一个参数[

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

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

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

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

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

    在 Bashese: [is a built-in command, and [[is a keyword: shell builtin 和 shell 关键字有什么区别?

  • <

  • &&||

    • [[ 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 扩展。因此,根据当前目录中的文件,它可能是 true 或 false。
    • [ ab = a\? ]: false,不是全局扩展
    • =and在and==中是相同的,但是是一个 Bash 扩展。[[[==
    • case ab in (a?) echo match; esac: POSIX 等价物
    • [[ ab =~ 'ab?' ]]: false,''在 Bash 3.2 及更高版本中失去魔力,并且未启用对 Bash 3.1 的兼容性(如BASH_COMPAT=3.1
    • [[ ab? =~ 'ab?' ]]: 真的
  • =~

    • [[ ab =~ ab? ]]: 真的。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的重要更正和补充。

于 2017-11-30T15:04:25.730 回答
66

[[ ]]有更多的特性——我建议你看一下Advanced Bash Scripting Guide以获得更多信息,特别是第 7 章中的扩展测试命令部分。测试。

顺便说一下,正如指南所指出的,[[ ]]它是在 ksh88(KornShell 的 1988 版本)中引入

于 2009-03-21T15:32:50.133 回答
19

哪个比较器、测试、支架或双支架最快?

双括号是一个“复合命令”,其中 test 和单括号是 shell 内置的(实际上是相同的命令)。因此,单括号和双括号执行不同的代码。

测试和单括号是最便携的,因为它们作为单独的外部命令存在。但是,如果您使用任何远程现代版本的 BASH,则支持双括号。

于 2009-03-21T15:34:15.340 回答
15

如果您喜欢遵循​​Google 的风格指南

测试[ … ], 和[[ … ]]

[[ … ]]优于[ … ],test/usr/bin/[.

[[ … ]][[减少了错误,因为和之间没有路径名扩展或分词]]。此外,[[ … ]]允许正则表达式匹配,而[ … ]不允许。

# This ensures the string on the left is made up of characters in
# the alnum character class followed by the string name.
# Note that the RHS should not be quoted here.
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
  echo "Match"
fi

# This matches the exact pattern "f*" (Does not match in this case)
if [[ "filename" == "f*" ]]; then
  echo "Match"
fi
# This gives a "too many arguments" error as f* is expanded to the
# contents of the current directory
if [ "filename" == f* ]; then
  echo "Match"
fi

有关血腥细节,请参阅http://tiswww.case.edu/php/chet/bash/FAQ上的 E14

于 2019-06-25T21:55:54.207 回答
15

在标题中明确包含“in Bash”的标记为“bash”的问题中,我对所有回复说你应该避免[[......]]因为它只在 bash 中有效!

确实,可移植性是主要的反对意见:如果你想编写一个在 Bourne 兼容的 shell 中工作的 shell 脚本,即使它们不是 bash,你应该避免[[...。]](如果你想在更严格的 POSIX shell 中测试你的 shell 脚本,我建议dash;虽然它是一个不完整的 POSIX 实现,因为它缺乏标准所需的国际化支持,但它也缺乏对发现的许多非 POSIX 构造的支持在 bash、ksh、zsh 等中)

我看到的另一个反对意见至少适用于 bash 的假设:那[[...]]有它自己的特殊规则,你必须学习,而[...]就像另一个命令一样。这又是真的(桑蒂利先生带来了显示所有差异的收据),但差异是好是坏是相当主观的。我个人发现,双括号结构让我可以使用(...)进行分组、&&布尔||逻辑、<比较>和不带引号的参数扩展。它就像它自己的小封闭世界,表达式的工作方式更像是在传统的非命令 shell 编程语言中。

我没有看到的一点是 ... 的这种行为[[与算术扩展构造...的行为]]完全一致,它POSIX 指定,并且还允许不带引号的括号以及布尔和不等式运算符(在此处执行数字而不是词法比较)。本质上,每当您看到双括号字符时,您都会获得相同的引号屏蔽效果。$(())

(Bash 和它的现代亲戚也使用((... ))- 没有前导$- 作为 C 风格的for循环头或执行算术运算的环境;这两种语法都不是 POSIX 的一部分。)

所以有一些很好的理由更喜欢[[...... ]];也有理由避免它,这可能适用于您的环境,也可能不适用。至于你的同事,“我们的风格指南这么说”是一个有效的理由,就它而言,但我也会从理解风格指南为什么推荐它的人那里寻找背景故事。

于 2020-07-14T13:05:18.583 回答
4

您无法使用的典型情况[[是在 autotools configure.ac 脚本中。括号有一个特殊的和不同的含义,所以你将不得不使用test代替[[[-- 注意 test 和[是同一个程序。

于 2015-03-21T17:23:12.980 回答
-1

[[ ]] 双括号在某些版本的SunOS下不受支持,并且在函数声明中完全不受支持:

GNU Bash,版本 2.02.0(1)-release (sparc-sun-solaris2.6)

于 2013-03-19T14:59:51.953 回答
-2

简而言之, [[ 更好,因为它不会分叉另一个进程。没有括号或单括号比双括号慢,因为它分叉了另一个进程。

于 2010-11-24T16:08:48.837 回答