Shell Spacing in Square Brackets

aces are required after [ and before ].

  1. [ is actually the name of a command, an alias for test. It’s not a special symbol, it’s just a command with an unusual name.
    $ help '['
    [: [ arg... ]
        Evaluate conditional expression.
    
        This is a synonym for the "test" builtin, but the last argument must
        be a literal `]', to match the opening `['.
  2. Because it’s an ordinary command name and not a special character, a space is required after the [. If you omit the space and write [foo the shell will search the $PATH for a command named [foo.
    $ [ foo = foo ] && echo true
    true
    $ [foo = foo] && echo true
    [foo: command not found
  3. For readability’s sake, [ expects its last argument to be exactly ]. Being an ordinary command-line argument, ] must have a space before it. If there’s no space then the bracket will become the last character of the previous argument, and [ will complain about its last argument not being ].
    $ [ foo = foo]
    bash: [: missing `]'
    $ [ foo = 'foo]'
    bash: [: missing `]'

[[ is a bash enhancement with more features than [, namely saner handling of unquoted variable names. It requires the a space on both ends, same as [. However [[ is in fact special shell syntax and is parsed differently. It’s not an “ordinary command” the way [ is.

  1. || operators for boolean tests and < and > for string comparisons. [ cannot do this because it is a regular command and &&||<, and > are not passed to regular commands as command-line arguments.
  2. It has a wonderful =~ operator for doing regular expression matches. With [ you might write
    if [ "$answer" = y -o "$answer" = yes ]

    With [[ you can write this as

    if [[ $answer =~ ^y(es)?$ ]]

    It even lets you access the captured groups which it stores in BASH_REMATCH. For instance, ${BASH_REMATCH[1]} would be “es” if you typed a full “yes” above.

  3. You get pattern matching aka globbing for free. Maybe you’re less strict about how to type yes. Maybe you’re okay if the user types y-anything. Got you covered:
    if [[ $ANSWER = y* ]]

Keep in mind that it is a bash extension, so if you are writing sh-compatible scripts then you need to stick with [. Make sure you have the #!/bin/bash shebang line for your script if you use double brackets.