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

我正在阅读bash关于if的例子,但一些例子用单方括号写:

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

其他带有双方括号的:

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

有什么不同?

82684 次浏览

你可以使用双方括号进行简单的正则表达式匹配,例如:

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

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

在单括号内用于条件测试(即[…]]),一些操作符,如单个=被所有shell所支持,而操作符==的使用则不被一些较旧的shell所支持。

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

编辑:我还应该注意:在bash中,总是使用双括号[[…]]],因为它比单括号更安全。我将用下面的例子来说明原因:

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

如果$var恰好是空的,那么这是脚本看到的:

if [ == "hello" ]; then

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

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

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

当你想让你的脚本在shell中可移植时,使用[]。如果你想要[]不支持且不需要可移植的条件表达式,请使用[[]]

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

看到

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

除非你是为POSIX sh编写,否则我们推荐[[

行为差异

在Bash 4.3.11中测试:

  • POSIX vs Bash扩展:

  • 常规命令vs魔法

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

      ]只是[的最后一个参数。

    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

  • < p > <

    • [[ a < b ]]:字典比较
    • [ a \< b ]:同上。\是必需的,否则会像其他命令一样重定向。Bash的扩展。
    • expr x"$x" \< x"$y" > /dev/null[ "$(expr x"$x" \< x"$y")" = 1 ]: POSIX的等价物,参见:如何测试字符串字典小于或等于在Bash?
  • &&||

    • [[ a = a && b = b ]]: true,逻辑而且
    • [ a = a && b = b ]:语法错误,&&解析为与命令分隔符cmd1 && cmd2
    • [ a = a ] && [ b = b ]: POSIX的可靠等价
    • [ a = a -a b = b ]:几乎等价,但被POSIX弃用,因为它是疯狂的,并且对于ab的某些值(如!()会失败,这些值将被解释为逻辑操作
  • < p > (

    • [[ (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操作符具有相同的优先级,不像[[ || ]][[ && ]]-o-a[
  • 字拆分和文件名生成扩展(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等价
  • < p > =

    • [[ ab = a? ]]: true,因为它执行模式匹配 (* ? [是魔法)。不会将glob扩展到当前目录下的文件。
    • [ ab = a? ]: a? glob扩展。因此,根据当前目录中的文件,可能为真或假。
    • [ ab = a\? ]: false,不是glob扩展
    • ===[[[中是相同的,但==是一个Bash扩展。
    • case ab in (a?) echo match; esac: POSIX等价
    • [[ ab =~ 'ab?' ]]: false,在Bash 3.2及以上版本中失去与''的魔力,并提供对Bash 3.1的兼容性未启用(如BASH_COMPAT=3.1)
    • [[ ab? =~ 'ab?' ]]:真
  • < p > =~

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

建议:始终使用[]

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

如果你使用[[ ]],你:

  • 失去的可移植性
  • 强迫读者学习另一个bash扩展的复杂性。[只是一个有奇怪名字的常规命令,没有特殊的语义涉及。

感谢Stephane Chazelas的重要修正和补充。

Bash手册说:

当与[[一起使用时,' < '和' > '操作符将按字典顺序排序 使用当前区域设置。test命令使用ASCII码 排序。< / p >

(test命令与[]相同)

  • [是一个类似printf的builtin。Bash语法希望在与命令相同的位置看到它。而]对Bash来说没什么,除了它是内置[所期望的事实。(man bash / SHELL内置命令)
  • [[是类似于if的keyword。Bash语法开始时也期望它与命令位于同一位置,但它不是执行它,而是进入条件上下文。]]也是结束此上下文的关键字。(man bash / SHELL语法/复合命令)

按顺序,bash尝试解析:用户别名>内置函数>用户功能>命令

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兼容性,这意味着你不是,所以不要关心get "more"兼容不兼容的脚本。