6

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?

kjo
  • 15,339
  • 25
  • 73
  • 114
  • See also: [What is the difference between [[ $a == z* ]] and $a == z* ]? – Stéphane Chazelas Mar 07 '13 at 12:40
  • Dang, usually the stackexchange interface saves me from posting duplicates, but this time it didn't: it showed exactly 0 suggestions for similar questions, which is pretty unusual, and maybe for this reason made me a bit over-confident... Sorry about that. – kjo Mar 07 '13 at 13:55

2 Answers2

5

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.

goldilocks
  • 87,661
  • 30
  • 204
  • 262
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
    += And the reason to not use it — it's slower then (again) built-in version [[. – poige Mar 07 '13 at 12:07
  • I am not sure it makes much sense to select between [ and [[ based on execution speed, considering that Bash is an interpreted language that shouldn't really be used for performance-critical applications anyway. YMMV of course, but consider the wisdom of "Preliminary optimization is the root of all evil" (D. Knuth.) – András Aszódi Nov 30 '22 at 14:12