Is double square brackets [[ ]] preferable over si

2018-12-31 14:39发布

问题:

A co-worker 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?

回答1:

[[ 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). 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). 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. Here is a good page about it: What is the difference between test, [ and [[ ? and Bash Tests



回答2:

[[ ]] 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.

Incidentally, as the guide notes, [[ ]] was introduced in ksh88 (the 1988 version of the Korn shell).



回答3:

Behavior differences

First, let\'s analyse the behavior differences between both. Tested in Bash 4.3.11.

  • POSIX vs Bash extension:

    • [ is POSIX
    • [[ is a Bash extension
  • regular command vs magic

    • [ is just a regular command with a weird name.

      ] is just an argument of [ that prevents further arguments from being used.

      Ubuntu 16.04 actually has an executable for it at /usr/bin/[ provided by coreutils, but the bash built-in version takes precedence.

      Nothing is altered in the way that Bash parses the command.

      In particular, < is redirection, && and || concatenate multiple commands, ( ) generates subshells unless escaped by \\, and word expansion happens as usual.

    • [[ X ]] is a single construct that makes X be parsed magically. <, &&, || 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

  • <

    • [[ a < b ]]: lexicographical comparison
    • [ a \\< b ]: Same as above. \\ required or else does redirection like for any other command. Bash extension.
    • I could not find a POSIX alternative to this, see: How to test strings for less than or equal?
  • && and ||

    • [[ a = a && b = b ]]: true, logical and
    • [ a = a && b = b ]: syntax error, && parsed as an AND command separator cmd1 && cmd2
    • [ a = a -a b = b ]: equivalent, but deprecated by POSIX
    • [ a = a ] && [ b = b ]: POSIX recommendation
  • (

    • [[ (a = a || a = b) && a = b ]]: false
    • [ ( a = a ) ]: syntax error, () is interpreted as a subshell
    • [ \\( a = a -o a = b \\) -a a = b ]: equivalent, but () is deprecated by POSIX
    • ([ a = a ] || [ a = b ]) && [ a = b ] POSIX recommendation
  • word splitting

    • x=\'a b\'; [[ $x = \'a b\' ]]: true, quotes not needed
    • x=\'a b\'; [ $x = \'a b\' ]: syntax error, expands to [ a b = \'a b\' ]
    • x=\'a b\'; [ \"$x\" = \'a b\' ]: equivalent
  • =

    • [[ ab = a? ]]: true, because it does pattern matching (* ? [ are magic). Does not glob expand to files in current directory.
    • [ ab = a? ]: a? glob expands. So may be true or false depending on the files in the current directory.
    • [ ab = a\\? ]: false, not glob expansion
    • = and == are the same in both [ and [[, but == is a Bash extension.
    • printf \'ab\' | grep -Eq \'a.\': POSIX ERE equivalent
    • [[ ab =~ \'ab?\' ]]: false, loses magic with \'\'
    • [[ ab? =~ \'ab?\' ]]: true
  • =~

    • [[ ab =~ ab? ]]: true, POSIX extended regular expression match, ? does not glob expand
    • [ a =~ a ]: syntax error
    • printf \'ab\' | grep -Eq \'ab?\': POSIX equivalent

Recommendation

I prefer to always use [].

There are POSIX equivalents for every [[ ]] construct I\'ve seen.

If you use [[ ]] you:

  • lose portability
  • force the reader to learn the intricacies of another bash extension. [ is just a regular command with a weird name, no special semantics are involved.


回答4:

From Which comparator, test, bracket, or double bracket, is fastest? (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). 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.



回答5:

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.



回答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.



回答7:

[[ ]] 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)