Difference between single and double square brackets in Bash
Single []
are posix shell compliant condition tests.
Double [[]]
are an extension to the standard []
and are supported by bash and other shells (e.g. zsh, ksh). They support extra operations (as well as the standard posix operations). For example: ||
instead of -o
and regex matching with =~
. A fuller list of differences can be found in the bash manual section on conditional constructs.
Use []
whenever you want your script to be portable across shells. Use [[]]
if you want conditional expressions not supported by []
and don't need to be portable.
Behavior differences
Tested in Bash 4.3.11:
-
POSIX vs Bash extension:
-
[
is POSIX -
[[
is a Bash extension inspired from Korn shell
-
-
regular command vs magic
-
[
is just a regular command with a weird name.]
is just the last argument of[
.
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 makesX
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 x"$x" \< x"$y" > /dev/null
or[ "$(expr x"$x" \< x"$y")" = 1 ]
: POSIX equivalents, 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 separatorcmd1 && cmd2
-
[ a = a ] && [ b = b ]
: POSIX reliable equivalent -
[ a = a -a b = b ]
: almost equivalent, but deprecated by POSIX because it is insane and fails for some values ofa
orb
like!
or(
which would be interpreted as logical operations
-
-
(
-
[[ (a = a || a = b) && a = b ]]
: false. Without( )
, would be true because[[ && ]]
has greater precedence than[[ || ]]
-
[ ( a = a ) ]
: syntax error,()
is interpreted as a subshell -
[ \( a = a -o a = b \) -a a = b ]
: equivalent, but()
,-a
, and-o
are deprecated by POSIX. Without\( \)
would be true because-a
has greater precedence than-o
-
{ [ a = a ] || [ a = b ]; } && [ a = b ]
non-deprecated POSIX equivalent. In this particular case however, we could have written just:[ a = a ] || [ a = b ] && [ a = b ]
because the||
and&&
shell operators have equal precedence unlike[[ || ]]
and[[ && ]]
and-o
,-a
and[
-
-
word splitting and filename generation upon expansions (split+glob)
-
x='a b'; [[ $x = 'a b' ]]
: true, quotes not needed -
x='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''
in Bash 3.2 and above and provided compatibility to bash 3.1 is not enabled (like withBASH_COMPAT=3.1
) -
[[ 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:
- lose portability
- force the reader to learn the intricacies of another bash extension.
[
is just a regular command with a weird name, no special semantics are involved.
Thanks to Stéphane Chazelas for important corrections and additions.
Inside single brackets for condition test (i.e. [ ... ]), some operators such as single =
is supported by all shells, whereas use of operator ==
is not supported by some of the older shells.
Inside double brackets for condition test (i.e. [[ ... ]]), there is no difference between using =
or ==
in old or new shells.
Edit: I should also note that: In bash, always use double brackets [[ ... ]] if possible, because it is safer than single brackets. I'll illustrate why with the following example:
if [ $var == "hello" ]; then
if $var happens to be null / empty, then this is what the script sees:
if [ == "hello" ]; then
which will break your script. The solution is to either use double brackets, or always remember to put quotes around your variables ("$var"
). Double brackets is better defensive coding practice.