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

rkm*_*max 142 bash if-statement

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

if [ -f $param ]
then
  #...
fi
Run Code Online (Sandbox Code Playgroud)

其他双方括号:

if [[ $? -ne 0 ]]
then
    start looking for errors in yourlog
fi
Run Code Online (Sandbox Code Playgroud)

有什么不同?

cmh*_*cmh 166

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

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

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

  • 我要补充一点,如果您的脚本不是以明确要求支持[[]]的shell的shebang开头的(例如,带有## // bin / bash`或## // usr / bin / env的bash) bash`),则应使用可移植选项。假设/ bin / sh支持此类扩展的脚本将在诸如Debian和Ubuntu等最新发行版的操作系统上中断,而情况并非如此。 (2认同)

Cir*_*四事件 74

行为差异

在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

  • <

  • &&||

    • [[ 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? ]]:true,因为它确实模式匹配(* ? [很神奇).不会将glob扩展为当前目录中的文件.
    • [ ab = a? ]:a?glob扩展.因此可能是真或假,具体取决于当前目录中的文件.
    • [ ab = a\? ]:false,不是glob扩展
    • ===在双方的同[[[,不过==是一个bash扩展.
    • case ab in (a?) echo match; esac:POSIX等价物
    • [[ ab =~ 'ab?' ]]:假4,失去魔力''
    • [[ ab? =~ 'ab?' ]]:是的
  • =~

    • [[ 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扩展的复杂性.[它只是一个带有奇怪名称的常规命令,不涉及特殊的语义.

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

²但是对于某些值ab(例如+index)失败,并进行数字比较,如果ab看起来像十进制整数.expr "x$a" '<' "x$b"适用于两者.

³并且也失败了某些ab类似的值!(.

4在bash 3.2及以上版本中提供与bash 3.1的兼容性未启用(如同BASH_COMPAT=3.1)

5尽管分组(这里与{...;}命令组,而不是(...)这将运行一个不必要的子shell)不是必要的,因为||&&壳运营商(而不是在||&& [[...]]运营商或-o/ -a [经营者)具有相同的优先级.所以[ a = a ] || [ a = b ] && [ a = b ]相同.

  • 神奇的总结.谢谢你的努力.然而,我不同意这项建议.它的可移植性与更一致和强大的语法相比.如果您可以在环境中要求bash> 4,则建议使用[[]]语法. (5认同)
  • 很好的答案,但我认为***建议**:总是使用`[]`*应该读作***我的偏好**:如果你不想失去便携性*使用`[]`.如[here](http://mywiki.wooledge.org/BashFAQ/031)所述:*如果担心POSIX或BourneShell的可移植性/一致性,则应使用旧语法.另一方面,如果脚本需要BASH,Zsh或KornShell,新语法通常更灵活,但不一定向后兼容.*我宁愿选择`[[ab = ~ab?]]`如果我可以并且不关心向后兼容性而不是`printf'ab'| grep -Eq'ab?'` (5认同)
  • @ A.Rick这将是所有"如何在语言Y中做X"的有效答案所以问题:-)当然,在该陈述中有一个"方便"隐含. (3认同)
  • "*我看到的每个[[]]构造都有POSIX等价物.*"对于任何[图灵完备](https://en.wikipedia.org/wiki/Universal_Turing_machine)语言都可以说同样的事情.面对这个星球. (2认同)

sam*_*hen 14

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

对于内部条件测试双括号(即[[...]]),存在使用没有任何区别===旧或新的炮弹.

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

if [ $var == "hello" ]; then
Run Code Online (Sandbox Code Playgroud)

如果$ var恰好是null/empty,那么这就是脚本看到的内容:

if [ == "hello" ]; then
Run Code Online (Sandbox Code Playgroud)

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

  • 除非您有充分的理由不这样做,否则在所有变量读取周围加上引号是一种更好的防御性编码实践,因为它适用于所有变量读取,而不仅仅是条件中的读取。如果硬盘驱动器名称包含空格(或类似内容),则 iTunes 安装程序错误会删除人们的文件。它也解决了你提到的问题。 (2认同)

Gil*_*not 11

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

看到

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

除非你是为POSIX sh写的,否则我们建议你[[.


tin*_*ino 6

  • [是一个builtin类似 printf 的东西。Bash 语法期望在与命令相同的位置看到它。对于 Bash 来说,除了内置]函数所期望的这一事实之外,什么都不是[。(man bash / shell 内置命令)
  • [[是一个keyword“如果”。Bash 语法也期望它在与命令相同的位置开始,但它不是执行它,而是进入条件上下文。并且]]也是结束此上下文的关键字。(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/[
Run Code Online (Sandbox Code Playgroud)
  • [我猜它会执行更多的解析代码。但我知道它调用相同数量的系统调用(用
  • [[在语法上,即使对于人类来说也更容易解析,因为它启动了一个上下文。对于算术条件,请考虑使用((.
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
Run Code Online (Sandbox Code Playgroud)

我建议使用[[:如果您不明确关心 posix 兼容性,则意味着您不关心,因此不必关心如何使不兼容的脚本“更”兼容。