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

前端 未结 9 2070
情话喂你
情话喂你 2020-11-22 03:58

A co-worker claimed recently in a code review that the [[ ]] construct is to be preferred over [ ] in constructs like

if [ \"`id -         


        
相关标签:
9条回答
  • 2020-11-22 04:26

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

    0 讨论(0)
  • 2020-11-22 04:28

    Behavior differences

    Some differences on 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.
      • expr a \< b > /dev/null: POSIX equivalent², see: How to test strings for lexicographic less than or equal in Bash?
    • && 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 and reliable equivalent
    • (

      • [[ (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 equivalent⁵
    • word splitting and filename generation upon expansions (split+glob)

      • 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='*'; [ $x = 'a b' ]: syntax error if there's more than one file in the current directory.
      • x='a b'; [ "$x" = 'a b' ]: POSIX 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.
      • case ab in (a?) echo match; esac: POSIX 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. No bash equivalent.
      • printf 'ab\n' | grep -Eq 'ab?': POSIX equivalent (single line data only)
      • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': POSIX equivalent.

    Recommendation: 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.

    ¹ Inspired from the equivalent [[...]] construct in the Korn shell

    ² but fails for some values of a or b (like + or index) and does numeric comparison if a and b look like decimal integers. expr "x$a" '<' "x$b" works around both.

    ³ and also fails for some values of a or b like ! or (.

    ⁴ in bash 3.2 and above and provided compatibility to bash 3.1 is not enabled (like with BASH_COMPAT=3.1)

    ⁵ though the grouping (here with the {...;} command group instead of (...) which would run an unnecessary subshell) is not necessary as the || and && shell operators (as opposed to the || and && [[...]] operators or the -o/-a [ operators) have equal precedence. So [ a = a ] || [ a = b ] && [ a = b ] would be equivalent.

    0 讨论(0)
  • 2020-11-22 04:29

    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.

    0 讨论(0)
  • 2020-11-22 04:40

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

    0 讨论(0)
  • 2020-11-22 04:45

    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.

    0 讨论(0)
  • 2020-11-22 04:48

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

    0 讨论(0)
提交回复
热议问题