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

时间:2012-11-24 15:56:58

标签: bash if-statement

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

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

其他双方括号:

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

有什么区别?

7 个答案:

答案 0 :(得分:156)

[]符合posix shell条件测试。

Double [[]]是标准[]的扩展,受bash和其他shell支持(例如zsh,ksh)。它们支持额外的操作(以及标准的posix操作)。例如:||代替-o,正则表达式与=~匹配。可以在bash manual section on conditional constructs中找到更完整的差异列表。

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

答案 1 :(得分:68)

行为差异

在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 :(得分:13)

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

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

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

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

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

if [ == "hello" ]; then

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

答案 3 :(得分:10)

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

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

除非您是为POSIX sh撰写的,否则我们建议使用[[

答案 4 :(得分:1)

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

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

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

答案 5 :(得分:1)

Bash manual说:

  

与[[,'&lt;'和'&gt;'运算符一起使用时,按字典顺序排序   使用当前的语言环境。 test命令使用ASCII   排序

(测试命令与[]相同)

答案 6 :(得分:0)

  • [ 是一个类似于 printf 的 builtin。 Bash 语法期望在与命令相同的位置看到它。并且 ] 对 Bash 没有任何意义,只是 [ 内置函数期望它。 (man bash / SHELL 内置命令)
  • [[ 是一个 keyword 就像 if。 Bash 语法开始也期望它与命令在同一位置,但它不是执行它,而是进入条件上下文。而 ]] 也是结束此上下文的关键字。 (man bash / SHELL GRAMMAR / 复合命令)

按顺序,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/[
  • [ 速度较慢 <= 我猜它会执行更多的解析代码。但我知道它调用了相同数量的系统调用(用
  • 测试
  • [[ 在语法上更容易解析,即使是人类,因为它启动了一个上下文。对于算术条件,请考虑使用 ((
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 兼容性,则意味着您不是,所以不要在意让不兼容的脚本“更多”兼容​​。