A co-worker claimed recently in a code review that the [[ ]]
construct is to be preferred over [ ]
in constructs like
if [ \"`id -
[[
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
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 neededx='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:
[
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.
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.
[[ ]]
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).
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.
[[ ]] 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)