8

The zsh man page, in its section on test (aka [), explicitly advises against using it at all, and urges readers to use [[ whenever possible.

The relevant section states:

The command attempts to implement POSIX and its extensions where these are specified. Unfortunately there are intrinsic ambiguities in the syntax; in particular there is no distinction between test operators and strings that resemble them. The standard attempts to resolve these for small numbers of arguments (up to four); for five or more arguments compatibility cannot be relied on. Users are urged wherever possible to use the '[[' test syntax which does not have these ambiguities.

I think I've come across similar advice for bash, but as I scan over the bash man page I can't find any "official" recommendation there on which form ([ or [[) to use. (Maybe I missed it?)

Is there any reason, other than backward compatibility with "older shells", for using [ in a bash script? Or to put it differently, does bash keep [ along with [[ for reasons other than backward compatibility?

2
4

The reason to prefer [ over [[ if sufficient is that [ is more stable between the different bash versions.

See man bash:

compat31

If set, bash changes its behavior to that of version 3.1 with respect to quoted arguments to the [[ conditional command's =~ operator.

compat32

If set, bash changes its behavior to that of version 3.2 with respect to locale-specific string comparison when using the [[ conditional command's < and > operators. Bash versions prior to bash-4.1 use ASCII collation and strcmp(3); bash-4.1 and later use the current locale's collation sequence and strcoll(3).

compat40

If set, bash changes its behavior to that of version 4.0 with respect to locale-specific string comparison when using the [[ conditional command's < and > operators (see previous item) and the effect of interrupting a command list.

Maybe it is also a little bit more common to users with ksh background.

Note about performance

'[[' is quicker as '['

  • For a singular invocation the speed difference has no commensurable impact, and your should prefer the '[' builtin.
  • If you it inside a bigger loop construct, you may think of replace '[' with '[['

For a measurement you may use the following compare script

let upperBound=$1
echo "check [,  ${upperBound} iterations"
let i=0
time while [ $i -lt ${upperBound} ] ; do let i++ ; done

echo; echo;
echo "check [[, ${upperBound} iterations"
let i=0
time while [[ $i < ${upperBound} ]] ; do let i++ ; done

Result of compare script

check [, 1000 iterations

real 0m0.031s
user 0m0.028s
sys 0m0.004s

check [[, 1000 iterations

real 0m0.000s
user 0m0.000s
sys 0m0.000s

1
  • 1
    += And the reason to not use it — it's slower then (again) built-in version [[.
    – poige
    Mar 7 '13 at 12:07
6

There are some subtle differences though:

#!/bin/bash

eg=whatever

if [ $eg == what* ]; then   # file glob test
    echo '[]'
fi

if [[ $eg == what* ]]; then   # pattern match test
    echo "[[]]"
fi      

Unless there is a file called "whatever" in the current directory, the first test will not pass, because the match is against a file glob of the current directory contents, whereas the second one is an actual string pattern match.

Not the answer you're looking for? Browse other questions tagged or ask your own question.