In my recent post I mentioned in passing, that you should be using double brackets
[[…]] for tests in
bash instead of single brackets.
This is the post where I explain why. I also talked about this briefly in my MacSysAdmin session: Scripting Bash
Double Brackets are a
Double brackets were originally introduced in
ksh and later adopted by
bash and other shells. To use double brackets your shebang should be
sh on macOS is
bash pretending to be
sh, double brackets will still work with the wrong shebang, but then your script might break on other platforms where a different shell might be pretending to be
sh. Consistent behavior across platforms is the main point why
sh is still around, so don’t use double brackets in
sh (or use
bash to use double brackets).
I go into detail on why to use
sh in this post: On the Shebang
Side note on syntax
In shell scripts you usually use tests in
while clauses. These are tedious to write in the interactive shell. The ‘and’ operator
&& will execute the following statement only if the preceding statement returns
0 (success). So you can use
&& to write simple
if … then … clauses in a single line.
if [ -d Documents ] then echo "found docs" fi
[ -d Documents ] && echo "found docs"
have the same effect. The second is much shorter, but as soon as the test or the command gets more complex you should revert to the longer syntax.
Alternatively, the ‘or’ operator
|| will only execute the following statement when the previous statement returns non-zero or fails:
[ -d Documents ] || echo "no docs"
is the same as
if [ ! -d Documents ] then echo "no docs" fi
What’s wrong with the single brackets?
The single bracket
[ is actually a command. It has the same functionality as the
test command, except that the last argument needs to be the closing square bracket
$ [ -d Documents && echo "found docs" -bash: [: missing `]' ~ $ [ -d Documents ] && echo "found docs" found docs $ test -d Documents && echo "found docs" found docs
bash on macoS both
[ are built-in commands, but as usual for built-in commands there are also executables
A single bracket test will fail when one of its arguments is empty and gets substituted to nothing:
$ a="abc" $ b="xyz" $ [ $a = $b ] || echo "unequal" unequal $ unset a $ [ $a = $b ] || echo "unequal" -bash: [: =: unary operator expected unequal
You can prevent this error by quoting the variables (always a prudent solution).
$ [ "$a" = "$b" ] || echo "unequal" unequal
Double brackets in
bash are not a command but a part of the language syntax. This means they can react more tolerantly to ‘disappearing’ arguments:
$ [[ $a = $b ]] || echo "unequal" unequal
You will also get an error if one of the arguments is substituted with a value with whitespace with single brackets, while double brackets can deal with this.
$ a="a" $ b="a space" $ [ $a = $b ] || echo "unequal" -bash: [: too many arguments unequal $ [[ $a = $b ]] || echo "unequal" unequal
= operator in
bash is for string comparison. To compare numerical values you need to use the
-ne (not equals),
-gt (greater than),
-ge (greater than or equal),
-lt (less than),
-le (less than or equal) operators. With double brackets you can also use two equals characters
== for a more C like syntax. (or, better, use
((…)) syntax for arithmetic expressions)
Also, when using the
= to assign variables, you cannot have spaces before and after the
=, while the spaces are required for the comparison operator (both with single and double brackets):
a="a" # no spaces b="b" # no spaces [ "$a" = "$b" ] # spaces! [[ $a = $b ]] # spaces!
Since the single bracket is a command, many characters it uses for its arguments need to be escaped to work properly:
$ [ ( "$a" = "$b" ) -o ( "$a" = "$c" ) ] -bash: syntax error near unexpected token `"$a"' $ [ \( "$a" = "$b" \) -o \( "$a" = "$c" \) ]
You could alternatively split this example into two tests:
[ "$a" = "$b" ] || [ "$a" = "$c" ].
Double brackets interpret these characters properly. You can also use the (again more C like)
|| operators instead of
[[ ( $a = $b ) || ( $a = $c ) ]]
In general, you can work around most of the issues with single bracket syntax, but the double bracket syntax is more straight forward and hence more legible and easier to type.
Double bracket features
Aside from the cleaner syntax, there are a few ‘bonus’ features you gain with double brackets.
With double brackets you can compare to
? wildcards, and bracket globbing
$ a="Documents" $ [[ $a = D* ]] && echo match match $ a=hat $ [[ $a = ?at ]] && echo match match $ [[ $a = [chrp]at ]] && echo match match
You can also use
> to compare strings lexicographically:
$ a=cat $ b=hat $ [[ $a < $b ]] && echo sorted sorted
And you get an operator
=~ for regular expressions:
$ a=cat $ b="the cat in the hat" $ [[ $a =~ ^.at ]] && echo match match $ [[ $b =~ ^.at ]] && echo match
Note that you should not quote the globbing patterns or the regex pattern.
- you should use
bashfor shell scripting on macOS
- when using bash, you should use double brackets instead of single brackets
- double brackets are safer, easier to type and read, and also add few neat features