Is double square brackets [[ ]] preferable over single square brackets [ ] in Bash?

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?


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


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


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 -ab = 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 -oa = b ) -aa = b ] : equivalent, but () is deprecated by POSIX
  • ([ a = a ] || [ a = b ]) && [ a = b ] POSIX recommendation
  • word splitting

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

  • [[ ab = a? ]] [[ ab = a? ]] : true, because it does pattern matching ( * ? [ are magic). Does not glob expand to files in current directory.
  • [ ab = 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
  • = and == are the same in both [ and [[ , but == is a Bash extension.
  • printf 'ab' | grep -Eq 'a.' : POSIX ERE equivalent
  • [[ ab =~ 'ab?' ]] [[ ab =~ 'ab?' ]] : false, loses magic with ''
  • [[ ab? =~ 'ab?' ]] [[ ab? =~ 'ab?' ]] : true
  • =~

  • [[ ab =~ ab? ]] [[ 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.
  • 链接地址: http://www.djcxy.com/p/2886.html

    上一篇: 如何使用SSH在远程机器上运行shell脚本?

    下一篇: 双方括号[[]]优于Bash中的单方括号[]吗?