Bash中单方括号和双方括号之间的区别


161

我正在阅读有关bash的示例,if但有些示例用单方括号括起来:

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

其他带有双方括号的:

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

有什么区别?


3
:您可以通过看这个问题的答案让你的答案 unix.stackexchange.com/questions/3831/...
阿米尔Naghizadeh

Answers:


188

单个[]是posix shell兼容条件测试。

Double [[]]是对标准的扩展,[]并由bash和其他shell(例如zsh,ksh)支持。它们支持额外的操作(以及标准的posix操作)。例如:||代替和-o与regex匹配=~在条件构造bash手册部分中可以找到更完整的差异列表。

使用[]时,你想要你的脚本是跨弹便携。使用[[]],如果你想不被支持的条件表达式[],不需要是便携式的。


6
我要补充一点,如果您的脚本不是以明确要求支持shell的shebang开头[[ ]](例如,使用bash #!/bin/bash#!/usr/bin/env bash),则应使用Portable选项。假定/ bin / sh支持此类扩展的脚本将在诸如Debian和Ubuntu等最新发行版的操作系统上中断,而实际情况并非如此。
戈登·戴维森

87

行为差异

在Bash 4.3.11中测试:

  • POSIX vs Bash扩展:

  • 常规命令与魔术

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

      ]只是其中的一个论点,[因此无法使用进一步的论点。

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

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

      特别<是重定向,&&||连接多个命令,( )除非通过进行转义\,否则将生成子外壳,并且单词扩展照常发生。

    • [[ 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 -a b = b ]:等效,但已被POSIX³弃用
    • [ a = a ] && [ b = b ]:POSIX和可靠的等效产品
  • (

    • [[ (a = a || a = b) && a = b ]]:错误
    • [ ( 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' ]]:正确,不需要引号
    • 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\? ]:错误,不是全局扩展
    • ===在双方的同[[[,不过==是一个bash扩展。
    • case ab in (a?) echo match; esac:POSIX等效
    • [[ ab =~ 'ab?' ]]:错误4,失去魔法''
    • [[ 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扩展的复杂性。[只是一个带有奇怪名称的常规命令,不涉及任何特殊的语义。

¹源自[[...]]Korn外壳中的等效构造

²,但对于aor的某些值b(例如+index)失败,并且如果aand b看起来像十进制整数,则进行数字比较。expr "x$a" '<' "x$b"都可以解决。

³也失败了的一些值ab喜欢!(

在bash 3.2及更高版本中为4,并且未提供与bash 3.1的兼容性(与一样BASH_COMPAT=3.1

5,尽管分组(此处使用{...;}命令组而不是(...)运行不必要的子shell)不是必需的,因为||and &&壳运算符(与||and && [[...]]运算符或-o/ -a [运算符相对)具有相同的优先级。因此[ a = a ] || [ a = b ] && [ a = b ]将是等效的。


3
我见过的每个[[]]构造都有POSIX等效项。 ”对于地球上的任何图灵完成语言,都可以说同样的话。
A. Rick

3
@ A.Rick可以有效回答所有“如何用语言Y进行X编程”这样的问题:-)当然,该语句中隐含了“方便”的含义。
西罗Santilli郝海东冠状病六四事件法轮功2016年

6
很棒的总结。感谢您的努力。但是,我不同意该建议。它的可移植性与更一致,更强大的语法相比。如果您的环境中要求bash> 4,则建议使用[[]]语法。
伯纳德

@Downvoters请解释,以便我可以学习和改进信息:-)
Ciro Santilli郝海东冠状病六四事件法轮功

8
很好的答案,但我认为建议:始终使用[]应该理解为我的偏好[]如果您不想失去便携性则使用。如前所述这里如果便携/ POSIX一致性或BourneShell是一个问题,应使用旧的语法。另一方面,如果脚本需要BASH,Zsh或KornShell,则新语法通常更灵活,但不一定向后兼容。我宁愿去,[[ ab =~ ab? ]]如果我可以并且不关心向后兼容性,那就比printf 'ab' | grep -Eq 'ab?'
MauricioRobayo

14

在用于状态测试的单括号内(即[...]),=所有shell都支持某些运算符,例如single ,而==某些较早的Shell不支持使用operator 。

里面的条件测试(即[[...]])双括号,有使用没有任何区别===旧或新的炮弹。

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

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

如果$ var恰好为null /空,那么脚本将显示以下内容:

if [ == "hello" ]; then

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


1
把周围的所有报价的变量读取,除非你有一个很好的理由不就是一个防守更好的编码习惯,因为它适用于所有读取的变量,而不仅仅是那些条件。如果硬盘驱动器名称包含空格(或类似名称),则iTunes安装程序错误会立即删除人们的文件。它还可以解决您提到的问题。
Chai T. Rex


1

您可以将双方括号用于正则表达式匹配,例如:

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

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


6
除非您已引用该模式,否则现在将其视为文字字符串。
ormaaj 2012年

非常真实 有时这让我
很烦

1

Bash手册说:

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

(测试命令与[]相同)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.