简体   繁体   English

在 Bash 中,双方括号 [[ ]] 是否优于单方括号 [ ]?

[英]Are double square brackets [[ ]] preferable over single square brackets [ ] in Bash?

A coworker claimed recently in a code review that the [[ ]] construct is to be preferred over [ ] in constructs like一位同事最近在代码审查中声称[[ ]]构造比[ ]在构造中更受欢迎,例如

if [ "`id -nu`" = "$someuser" ] ; then
     echo "I love you madly, $someuser"
fi

He couldn't provide a rationale.他无法提供理由。 Is there one?有吗?

[[ has fewer surprises and is generally safer to use. [[惊喜较少,通常使用起来更安全。 But it is not portable - POSIX doesn't specify what it does and only some shells support it (beside bash, I heard ksh supports it too).但它不是可移植的——POSIX 没有指定它的作用,只有一些 shell 支持它(除了 bash,我听说 ksh 也支持它)。 For example, you can do例如,你可以做

[[ -e $b ]]

to test whether a file exists.测试文件是否存在。 But with [ , you have to quote $b , because it splits the argument and expands things like "a*" (where [[ takes it literally).但是对于[ ,您必须引用$b ,因为它会拆分参数并扩展诸如"a*"类的内容(其中[[字面意思是)。 That has also to do with how [ can be an external program and receives its argument just normally like every other program (although it can also be a builtin, but then it still has not this special handling).这也与[如何成为外部程序并像其他所有程序一样正常接收其参数有关(尽管它也可以是内置程序,但它仍然没有这种特殊处理)。

[[ also has some other nice features, like regular expression matching with =~ along with operators like they are known in C-like languages. [[还具有其他一些不错的功能,例如与=~匹配的正则表达式以及类似 C 语言中已知的运算符。 Here is a good page about it: What is the difference between test, [ and [[ ?这是一个很好的页面: test, [[[之间有什么区别? and Bash TestsBash 测试

Behavior differences行为差异

Some differences on Bash 4.3.11: Bash 4.3.11 的一些差异:

  • POSIX vs Bash extension: POSIX 与 Bash 扩展:

  • regular command vs magic常规命令与魔法

    • [ is just a regular command with a weird name. [只是一个带有奇怪名称的常规命令。

      ] is just the last argument of [ . ]只是[的最后一个参数。

      Ubuntu 16.04 actually has an executable for it at /usr/bin/[ provided by coreutils , but the bash built-in version takes precedence. Ubuntu 16.04 实际上在coreutils提供的/usr/bin/[中有一个可执行文件,但 bash 内置版本优先。

      Nothing is altered in the way that Bash parses the command. Bash 解析命令的方式没有任何改变。

      In particular, < is redirection, && and ||特别是, <是重定向, &&|| concatenate multiple commands, ( ) generates subshells unless escaped by \ , and word expansion happens as usual.连接多个命令, ( )生成子shell,除非被\转义,并且单词扩展照常发生。

    • [[ X ]] is a single construct that makes X be parsed magically. [[ X ]]是一个使X被神奇地解析的单一结构。 < , && , || < , && , || and () are treated specially, and word splitting rules are different.()被特殊对待,分词规则不同。

      There are also further differences like = and =~ .还有其他区别,例如==~

    In Bashese: [ is a built-in command, and [[ is a keyword: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword在 Bashese 中: [是内置命令, [[是关键字: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && and || &&||

    • [[ a = a && b = b ]] : true, logical and [[ a = a && b = b ]] :真实,逻辑
    • [ a = a && b = b ] : syntax error, && parsed as an AND command separator cmd1 && cmd2 [ a = a && b = b ] : 语法错误, &&被解析为 AND 命令分隔符cmd1 && cmd2
    • [ a = a ] && [ b = b ] : POSIX reliable equivalent [ a = a ] && [ b = b ] :POSIX 可靠等价物
    • [ a = a -ab = b ] : almost equivalent, but deprecated by POSIX because it is insane and fails for some values of a or b like ! [ a = a -ab = b ] :几乎等价,但被 POSIX 弃用,因为它很疯狂,并且对于ab的某些值失败,例如! or ( which would be interpreted as logical operations(将被解释为逻辑运算
  • (

    • [[ (a = a || a = b) && a = b ]] : false. [[ (a = a || a = b) && a = b ]] : 错误。 Without ( ) , would be true because [[ && ]] has greater precedence than [[ || ]]如果没有( ) ,则为真,因为[[ && ]]的优先级高于[[ || ]] [[ || ]]
    • [ ( a = a ) ] : syntax error, () is interpreted as a subshell [ ( a = a ) ] : 语法错误, ()被解释为子shell
    • [ \( a = a -oa = b \) -aa = b ] : equivalent, but () , -a , and -o are deprecated by POSIX. [ \( a = a -oa = b \) -aa = b ] :等效,但()-a-o已被 POSIX 弃用。 Without \( \) would be true because -a has greater precedence than -o没有\( \)将是真的,因为-a-o具有更高的优先级
    • { [ a = a ] || [ a = b ]; } && [ a = b ] { [ a = a ] || [ a = b ]; } && [ a = b ] non-deprecated POSIX equivalent. { [ a = a ] || [ a = b ]; } && [ a = b ]不推荐使用的 POSIX 等效项。 In this particular case however, we could have written just: [ a = a ] || [ a = b ] && [ a = b ]然而,在这种特殊情况下,我们可以只写: [ a = a ] || [ a = b ] && [ a = b ] [ a = a ] || [ a = b ] && [ a = b ] because the || [ a = a ] || [ a = b ] && [ a = b ]因为|| and && shell operators have equal precedence unlike [[ || ]]&& shell 运算符具有相同的优先级,不像[[ || ]] [[ || ]] and [[ && ]] and -o , -a and [ [[ || ]][[ && ]]-o-a[
  • word splitting and filename generation upon expansions (split+glob)扩展时的分词和文件名生成 (split+glob)

    • x='a b'; [[ $x = 'ab' ]] x='a b'; [[ $x = 'ab' ]] : true, quotes not needed x='a b'; [[ $x = 'ab' ]] : true,不需要引号
    • x='a b'; [ $x = 'ab' ] x='a b'; [ $x = 'ab' ] : syntax error, expands to [ ab = 'ab' ] x='a b'; [ $x = 'ab' ] : 语法错误,扩展为[ ab = 'ab' ]
    • x='*'; [ $x = 'ab' ] x='*'; [ $x = 'ab' ] : syntax error if there's more than one file in the current directory. x='*'; [ $x = 'ab' ] : 如果当前目录中有多个文件,则语法错误。
    • x='a b'; [ "$x" = 'ab' ] x='a b'; [ "$x" = 'ab' ] : POSIX equivalent x='a b'; [ "$x" = 'ab' ] : POSIX 等价物
  • =

    • [[ ab = a? ]] [[ ab = a? ]] : true, because it does pattern matching ( *? [ are magic). [[ ab = a? ]] :是的,因为它会进行模式匹配*? [很神奇)。 Does not glob expand to files in current directory.不 glob 扩展到当前目录中的文件。
    • [ ab = a? ] [ ab = a? ] : a? [ ab = a? ]a? glob expands.球体扩大。 So may be true or false depending on the files in the current directory.所以可能是真或假,具体取决于当前目录中的文件。
    • [ ab = a\? ] [ ab = a\? ] : false, not glob expansion [ ab = a\? ] : false,不是全局扩展
    • = and == are the same in both [ and [[ , but == is a Bash extension. ===[[[中相同,但==是 Bash 扩展。
    • case ab in (a?) echo match; esac case ab in (a?) echo match; esac : POSIX equivalent case ab in (a?) echo match; esac : POSIX 等价物
    • [[ ab =~ 'ab?' ]] [[ ab =~ 'ab?' ]] : false, loses magic with '' in Bash 3.2 and above and provided compatibility to bash 3.1 is not enabled (like with BASH_COMPAT=3.1 ) [[ ab =~ 'ab?' ]] : false,在 Bash 3.2 及更高版本中失去魔法,并提供与'' 3.1 的兼容性未启用(如BASH_COMPAT=3.1
    • [[ ab? =~ 'ab?' ]] [[ ab? =~ 'ab?' ]] : true [[ ab? =~ 'ab?' ]] :真
  • =~

    • [[ ab =~ ab? ]] [[ ab =~ ab? ]] : true, POSIX extended regular expression match, ? [[ ab =~ ab? ]] : true,POSIX 扩展正则表达式匹配, ? does not glob expand不全局扩展
    • [ a =~ a ] : syntax error. [ a =~ a ] :语法错误。 No bash equivalent.没有 bash 等效项。
    • printf 'ab\n' | grep -Eq 'ab?' : POSIX equivalent (single line data only) : POSIX 等效项(仅单行数据)
    • awk 'BEGIN{exit?(ARGV[1] ~ ARGV[2])}' ab 'ab?' : POSIX equivalent. : POSIX 等价物。

Recommendation: always use []建议:始终使用[]

There are POSIX equivalents for every [[ ]] construct I've seen.我见过的每个[[ ]]构造都有 POSIX 等效项。

If you use [[ ]] you:如果你使用[[ ]]你:

  • lose portability失去便携性
  • force the reader to learn the intricacies of another bash extension.迫使读者了解另一个 bash 扩展的复杂性。 [ is just a regular command with a weird name, no special semantics are involved. [只是一个名称怪异的常规命令,不涉及特殊语义。

Thanks to Stéphane Chazelas for important corrections and additions.感谢Stéphane Chazelas的重要更正和补充。

[[ ]] has more features - I suggest you take a look at the Advanced Bash Scripting Guide for more info, specifically the extended test command section in Chapter 7. Tests . [[ ]]有更多功能 - 我建议您查看Advanced Bash 脚本指南以获取更多信息,特别是第 7 章中的扩展测试命令部分。测试。

Incidentally, as the guide notes, [[ ]] was introduced in ksh88 (the 1988 version of the Korn shell).顺便说一下,正如指南所指出的, [[ ]]是在 ksh88(Korn shell 的 1988 年版本)中引入的。

From Which comparator, test, bracket, or double bracket, is fastest? 哪个比较器、测试、支架或双支架最快? ( http://bashcurescancer.com ) http://bashcurescancer.com

The double bracket is a “compound command” where as test and the single bracket are shell built-ins (and in actuality are the same command).双括号是“复合命令”,其中 test 和单括号是 shell 内置(实际上是相同的命令)。 Thus, the single bracket and double bracket execute different code.因此,单括号和双括号执行不同的代码。

The test and single bracket are the most portable as they exist as separate and external commands.测试和单括号是最便携的,因为它们作为单独的外部命令存在。 However, if your using any remotely modern version of BASH, the double bracket is supported.但是,如果您使用 BASH 的任何远程现代版本,则支持双括号。

In a question tagged 'bash' that explicitly has "in Bash" in the title, I'm a little surprised by all of the replies saying you should avoid [[ ... ]] because it only works in bash!在标题中明确包含“in Bash”的标记为“bash”的问题中,我对所有回复说你应该避免[[ ... ]]感到有点惊讶,因为它只适用于 bash!

It's true that portability is the primary objection: if you want to write a shell script which works in Bourne-compatible shells even if they aren't bash, you should avoid [[ ... ]] .确实,可移植性是主要的反对意见:如果你想编写一个 shell 脚本,即使它们不是 bash,它也可以在 Bourne 兼容的 shell 中工作,你应该避免[[ ... ]] (And if you want to test your shell scripts in a more strictly POSIX shell, I recommend dash ; though it is an incomplete POSIX implementation since it lacks the internationalization support required by the standard, it also lacks support for the many non-POSIX constructs found in bash, ksh, zsh, etc.) (如果你想在更严格的 POSIX shell 中测试你的 shell 脚本,我推荐dash ;虽然它是一个不完整的 POSIX 实现,因为它缺乏标准所需的国际化支持,它也缺乏对许多非 POSIX 结构的支持见于 bash、ksh、zsh 等)

The other objection I see is at least applicable within the assumption of bash: that [[ ... ]] has its own special rules which you have to learn, while [ ... ] acts like just another command.我看到的另一个反对意见至少适用于 bash 的假设: [[ ... ]]有自己的特殊规则,您必须学习,而[ ... ]就像另一个命令一样。 That is again true (and Mr. Santilli brought the receipts showing all the differences), but it's rather subjective whether the differences are good or bad.这又是真的(桑蒂利先生带来了显示所有差异的收据),但差异是好是坏是相当主观的。 I personally find it freeing that the double-bracket construct lets me use ( ... ) for grouping, && and ||我个人发现双括号结构让我可以使用( ... )进行分组, &&|| for Boolean logic, < and > for comparison, and unquoted parameter expansions.对于 Boolean 逻辑, <>用于比较,以及不带引号的参数扩展。 It's like its own little closed-off world where expressions work more like they do in traditional, non-command-shell programming languages.它就像它自己的小封闭世界,表达式的工作方式更像是在传统的非命令 shell 编程语言中。

A point I haven't seen raised is that this behavior of [[ ... ]] is entirely consistent with that of the arithmetic expansion construct $(( ... )) , which is specified by POSIX, and also allows unquoted parentheses and Boolean and inequality operators (which here perform numeric instead of lexical comparisons).我没有看到的一点是[[ ... ]]的这种行为与 POSIX 指定的算术扩展构造$(( ... ))的行为完全一致,并且还允许使用不带引号的括号和 Boolean 和不等式运算符(这里执行数字而不是词法比较)。 Essentially, any time you see the doubled bracket characters you get the same quote-shielding effect.本质上,每当您看到双括号字符时,您都会获得相同的引号屏蔽效果。

(Bash and its modern relatives also use (( ... )) – without the leading $ – as either a C-style for loop header or an environment for performing arithmetic operations; neither syntax is part of POSIX.) (Bash 及其现代亲属也使用(( ... )) - 没有前导$ - 作为 C 风格for循环 header 或执行算术运算的环境;这两种语法都不是 POSIX 的一部分。)

So there are some good reasons to prefer [[ ... ]] ;所以有一些很好的理由更喜欢[[ ... ]] there are also reasons to avoid it, which may or may not be applicable in your environment.也有理由避免它,这可能适用于您的环境,也可能不适用。 As to your coworker, "our style guide says so" is a valid justification, as far as it goes, but I'd also seek out backstory from someone who understands why the style guide recommends what it does.至于你的同事,“我们的风格指南这么说”是一个有效的理由,就它而言,但我也会从理解风格指南为什么推荐它的人那里寻找背景故事。

If you are into following Google's style guide :如果您喜欢遵循Google 的风格指南

Test, [ and [[测试, [[[

[[... ]] reduces errors as no path name expansion or word splitting takes place between [[ and ]] , and [[... ]] allows for regular expression matching where [... ] does not. [[... ]]减少了错误,因为在[[]]之间没有发生路径名扩展或分词,并且[[... ]]允许在[... ]不允许的情况下进行正则表达式匹配。

# This ensures the string on the left is made up of characters in the
# alnum character class followed by the string name.
# Note that the RHS should not be quoted here.
# For the gory details, see
# E14 at https://tiswww.case.edu/php/chet/bash/FAQ
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
  echo "Match"
fi

# This matches the exact pattern "f*" (Does not match in this case)
if [[ "filename" == "f*" ]]; then
  echo "Match"
fi

# This gives a "too many arguments" error as f* is expanded to the
# contents of the current directory
if [ "filename" == f* ]; then
  echo "Match"
fi

A typical situation where you cannot use [[ is in an autotools configure.ac script, there brackets has a special and different meaning, so you will have to use test instead of [ or [[ -- Note that test and [ are the same program.您不能使用[[的典型情况是在 autotools configure.ac 脚本中,括号具有特殊且不同的含义,因此您必须使用test而不是[[[ -- 请注意 test 和[是相同的程序。

I am surprised that I haven't seen this issue raised sooner, but consider:我很惊讶我没有看到这个问题更早提出,但请考虑:

$ n=5
$ [[ $n -gt 0 ]] && echo $n is positive
5 is positive
$ n=foo
$ [[ $n -gt 0 ]] && echo $n is positive
$ [ "$n" -gt 0 ] && echo $n is positive
bash: [: foo: integer expression expected

The lack of error message from the line [[ $n -gt 0 ]] renders [[ completely unusable. [[ $n -gt 0 ]]行缺少错误消息导致[[完全无法使用。 Perhaps I'm being too harsh and the correct response is to simply avoid integer comparisons in [[ , but I strongly advise against [[ for this reason.也许我太苛刻了,正确的反应是简单地避免在[[中进行 integer 比较,但我强烈建议不要[[出于这个原因。

[[ ]] double brackets are unsuported under certain version of SunOS and totally unsuported inside function declarations by: GNU bash, version 2.02.0(1)-release (sparc-sun-solaris2.6) [[ ]] 双括号在某些版本的 SunOS 下不受支持,在 function 声明中完全不受支持:GNU bash,版本 2.02.0(1)-release (sparc-sun-solaris2.6)

In a nutshell, [[ is better because it doesn't fork another process.简而言之, [[ 更好,因为它不会分叉另一个进程。 No brackets or a single bracket is slower than a double bracket because it forks another process.没有括号或单括号比双括号慢,因为它分叉了另一个进程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM