双方括号[[]]比Bash中的单方括号[]更好吗?

时间:2009-03-21 15:28:45

标签: bash if-statement syntax

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

等构造中的[ ]更受欢迎
if [ "`id -nu`" = "$someuser" ] ; then 
     echo "I love you madly, $someuser"
fi

他无法提供理由。有吗?

9 个答案:

答案 0 :(得分:511)

[[减少意外,通常使用起来更安全。但它不可移植 - POSIX没有指定它做什么,只有一些shell支持它(除了bash,我听说ksh也支持它)。例如,你可以做

[[ -e $b ]]

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

[[还有一些其他不错的功能,例如与=~匹配的正则表达式以及类似C语言的运算符。这是一个很好的页面:What is the difference between test, [ and [[ ?Bash Tests

答案 1 :(得分:69)

行为差异

关于Bash 4.3.11的一些区别:

  • POSIX vs Bash扩展名:

  • 常规命令vs魔法

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

      ]只是[的一个参数,可以防止使用其他参数。

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

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

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

    • [[ X ]]是一个让X神奇地解析的构造。特别对待<&&||(),并且分词规则也不同。

      还有其他差异,例如==~

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

  • <

  • &&||

    • [[ a = a && b = b ]]:true,逻辑
    • [ a = a && b = b ]:语法错误,&&被解析为AND命令分隔符cmd1 && cmd2
    • [ a = a -a b = b ]:等效,但已由POSIX³弃用
    • [ a = a ] && [ b = b ]:POSIX和可靠的等价物
  • (

    • [[ (a = a || a = b) && a = b ]]:false
    • [ ( a = a ) ]:语法错误,()被解释为子shell
    • [ \( a = a -o a = b \) -a a = b ]:等效,但POSIX
    • 弃用()
    • { [ a = a ] || [ a = b ]; } && [ a = b ] POSIX等效 5
  • 扩展时的单词拆分和文件名生成(split + glob)

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

    • [[ ab = a? ]]:是的,因为pattern matching* ? [是神奇的)。不会将glob扩展为当前目录中的文件。
    • [ ab = a? ]a? glob扩展。因此可能是真或假,具体取决于当前目录中的文件。
    • [ ab = a\? ]:false,不是全局展开
    • ===[[[中都相同,但==是Bash扩展名。
    • case ab in (a?) echo match; esac:POSIX等效
    • [[ ab =~ 'ab?' ]]:false 4 ,与''
    • 失去了魔法
    • [[ ab? =~ 'ab?' ]]:true
  • =~

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

推荐:始终使用[]

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

如果你使用[[ ]]

  • 失去便携性
  • 强迫读者学习另一个bash扩展的复杂性。 [只是一个带有奇怪名称的常规命令,不涉及特殊的语义。

¹灵感来自Korn shell中等效的[[...]]构造

²但对ab(如+index)的某些值失败,并且如果a和{进行数字比较{1}}看起来像十进制整数。 b适用于两者。

³并且expr "x$a" '<' "x$b"a的某些值(例如b!也失败了。

在bash 3.2及更高版本中

4 并且未启用与bash 3.1的兼容性(与(一样)

5 虽然分组(这里使用BASH_COMPAT=3.1命令组而不是{...;}运行不必要的子shell)不是必需的{{1 }}和(...) shell运算符(而不是||&& ||运算符或&& / [[...]] -o运营商)具有相同的优先权。所以-a是等价的。

答案 2 :(得分:51)

[[ ]]有更多功能 - 建议您查看Advanced Bash Scripting Guide了解更多信息,特别是extended test command中的 Chapter 7. Tests 部分

顺便提一下,正如指南所说,[[ ]]是在ksh88(1988年版的Korn shell)中引入的。

答案 3 :(得分:8)

来自Which comparator, test, bracket, or double bracket, is fastest?http://bashcurescancer.com

  

双括号是“复合物   命令“测试和单一的地方   括号是shell内置的(并且在   现实是相同的命令)。从而,   单支架和双支架   执行不同的代码。

     

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

答案 4 :(得分:7)

在标题为“ bash”的标题中明确带有“ in Bash”的问题中,我对所有应避免使用[[ ... ]]的答复感到有些惊讶它只能在bash中使用!

的确,移植性是主要的反对意见:如果您想编写一个在Bourne兼容的shell中也可以工作的shell脚本,即使它们不是bash,也应避免使用[[ ... {{1} }。 (并且,如果您想在更严格的POSIX shell中测试您的shell脚本,我建议使用]];尽管它是不完整的POSIX实现,因为它缺少标准所要求的国际化支持,但它也缺少对许多支持的支持。在bash,ksh,zsh等中找到非POSIX结构)

我看到的另一个异议至少可以适用于bash假设:dash ... [[有自己的特殊规则,而]]则需要学习。 .. [就像另一个命令一样。再次是对的(Santilli先生带来了显示所有差异的收据),但是差异的好坏是相当主观的。我个人发现双括号结构使我可以使用] ... (进行分组,)&&进行布尔逻辑,||<用于比较,以及未引用的参数扩展。就像它自己的封闭世界一样,表达式的工作方式就像在传统的非命令外壳编程语言中一样。

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

(Bash及其现代亲戚也使用$(( ... )) –不使用前导(( –作为C样式))循环标头或环境用于执行算术运算;两种语法都不是POSIX的一部分。)

因此,有一些充分的理由偏爱$ ... for;还有避免它的原因,这可能适用于您的环境,也可能不适用。对于您的同事而言,“我们的风格指南如此说”是一个合理的证明,但就我所能理解的人而言,我还会从了解该风格指南为何推荐其用途的人那里寻找背景故事。

答案 5 :(得分:1)

一种典型的情况,你不能使用[[在autotools configure.ac脚本中,括号有一个特殊和不同的含义,所以你必须使用test而不是[或[[ - 注意]那个测试和[是同一个程序。

答案 6 :(得分:1)

如果您关注以下Google's style guide

测试,[和[[

  

[[...]]减少了错误,因为在[[和]]之间没有路径名扩展或单词拆分,并且[[...]]允许在[...]不存在的情况下进行正则表达式匹配。 / p>

# 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.
# For the gory details, see
# E14 at https://tiswww.case.edu/php/chet/bash/FAQ
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

答案 7 :(得分:-1)

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

答案 8 :(得分:-1)

[[]]双括号在某些版本的SunOS下不受支持,并且完全不支持内部函数声明: GNU bash,版本2.02.0(1)-release(sparc-sun-solaris2.6)