Bash Wildcards – Difference Between [[ $a == z* ]] and [ $a == z* ]?

bashshell-scripttestwildcards

Is there is any difference between these two.

[[ $a == z* ]]

and

[ $a == z* ] 

Can I have an example where they would have different outputs?

Furthermore, how does the working of [[ ]] differs from [ ]?

Best Answer

The difference between [[ … ]] and [ … ] is mostly covered in Why does parameter expansion with spaces without quotes work inside double brackets "[[" but not inside single brackets "["?. Crucially, [[ … ]] is special syntax, whereas [ is a funny-looking name for a command. [[ … ]] has special syntax rules for what's inside, [ … ] doesn't.

With the added wrinkle of a wildcard, here's how [[ $a == z* ]] is evaluated:

  1. Parse the command: this is the [[ … ]] conditional construct around the conditional expression $a == z*.
  2. Parse the conditional expression: this is the == binary operator, with the operands $a and z*.
  3. Expand the first operand into the value of the variable a.
  4. Evaluate the == operator: test if the value of the variable a matches the pattern z*.
  5. Evaluate the conditional expression: its result is the result of the conditional operator.
  6. The command is now evaluated, its status is 0 if the conditional expression was true and 1 if it was false.

Here's how [ $a == z* ] is evaluated:

  1. Parse the command: this is the [ command with the arguments formed by evaluating the words $a, ==, z*, ].
  2. Expand $a into the value of the variable a.
  3. Perform word splitting and filename generation on the parameters of the command.
    • For example, if the value of a is the 6-character string foo b* (obtained by e.g. a='foo b*') and the list of files in the current directory is (bar, baz, qux, zim, zum), then the result of the expansion is the following list of words: [, foo, bar, baz, ==, zim, zum, ].
  4. Run the command [ with the parameters obtained in the previous step.
    • With the example values above, the [ command complains of a syntax error and returns the status 2.

Note: In [[ $a == z* ]], at step 3, the value of a does not undergo word splitting and filename generation, because it's in a context where a single word is expected (the left-hand argument of the conditional operator ==). In most cases, if a single word makes sense at that position then variable expansion behaves like it does in double quotes. However, there's an exception to that rule: in [[ abc == $a ]], if the value of a contains wildcards, then abc is matched against the wildcard pattern. For example, if the value of a is a* then [[ abc == $a ]] is true (because the wildcard * coming from the unquoted expansion of $a matches bc) whereas [[ abc == "$a" ]] is false (because the ordinary character * coming from the quoted expansion of $a does not match bc). Inside [[ … ]], double quotes do not make a difference, except on the right-hand side of the string matching operators (=, ==, != and =~).

Related Question