Bash中单方括号和双方括号之间的差异



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

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

其他带双方括号的:

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

有什么区别?

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

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

只要您希望脚本可以跨shell移植,就可以使用[]。如果想要[]不支持的条件表达式并且不需要可移植,请使用[[]]

行为差异

在Bash 4.3.11:中测试

  • POSIX与Bash扩展:

    • [是POSIX
    • [[是受KornShell启发的Bash扩展
  • 常规命令与魔术

    • [只是一个名称怪异的常规命令。

      CCD_ 14只是CCD_ 15的最后一个自变量。

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

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

    特别地,<是重定向,&&||连接多个命令,( )生成子shell,除非被转义,否则字扩展照常进行。

    • [[ X ]]是使X能够被神奇地解析的单个构造。<&&||()被特殊处理,并且分词规则不同。

      还存在进一步的差异,如CCD_ 28和CCD_。

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

  • <

    • [[ a < b ]]:词典学比较
    • [ a < b ]:同上。是必需的,否则会像执行任何其他命令一样执行重定向。Bash扩展
    • expr x"$x" < x"$y" > /dev/null[ "$(expr x"$x" < x"$y")" = 1 ]:POSIX等价物,请参阅:如何在Bash中测试字典小于或等于的字符串
  • CCD_ 38和CCD_ 39

    • [[ a = a && b = b ]]:真,逻辑
    • [ a = a && b = b ]:语法错误,&&解析为AND命令分隔符cmd1 && cmd2
    • [ a = a ] && [ b = b ]:POSIX可靠等效
    • [ a = a -a b = b ]:几乎等同,但被POSIX否决,因为它是疯狂的,并且对于ab的某些值(如!()失败,这些值将被解释为逻辑运算
  • (

    • [[ (a = a || a = b) && a = b ]]:错误。如果没有( ),则为真,因为[[ && ]]的优先级高于[[ || ]]
    • [ ( a = a ) ]:语法错误,()被解释为子shell
    • [ ( a = a -o a = b ) -a a = b ]:等效,但POSIX不赞成使用()-a-o。如果没有( )则为真,因为-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等效
  • =

    • [[ ab = a? ]]:true,因为它进行模式匹配(* ? [很神奇)。不全局扩展到当前目录中的文件
    • [ ab = a? ]:a?球展开。因此,根据当前目录中的文件,可能为true或false
    • [ 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?' ]]:真
  • =~

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

建议:始终使用[]

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

如果你使用[[ ]]你:

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

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

[[是一个类似于(但比)[命令的bash关键字。

参见

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

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

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

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

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

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

如果$var恰好为null/空,那么脚本会看到以下内容:

if [ == "hello" ]; then

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

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

按照顺序,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/[
  • [较慢<=我想它会执行更多的解析代码。但我知道它调用相同数量的系统调用(用
  • CCD_ 120在语法上更容易解析,即使对人类来说也是如此。对于算术条件,考虑使用((
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兼容性,那就意味着你不关心,所以不关心得到"更多";与不兼容的脚本兼容。

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

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

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

Bash手册上说:

与[[一起使用时,'<'和'>'运算符按字典顺序排序使用当前区域设置。测试命令使用ASCII订购。

(测试命令与[]相同)

相关内容

  • 没有找到相关文章

最新更新