The [
command is an ordinary command. Although most shells provide it as a built-in for efficiency, it obeys the shell's normal syntactic rules. [
is exactly equivalent to test
, except that [
requires a ]
as its last argument and test
doesn't.
The double brackets [[ … ]]
are special syntax. They were introduced in ksh (several years after [
) because [
can be troublesome to use correctly and [[
allows some new nice additions that use shell special characters. For example, you can write
[[ $x = foo && $y = bar ]]
because the entire conditional expression is parsed by the shell, whereas [ $x = foo && $y = bar ]
would first be split into two commands [ $x = foo
and $y = bar ]
separated by the &&
operator. Similarly double brackets enable things like the pattern matching syntax, e.g. [[ $x == a* ]]
to test whether the value of x
starts with a
; in single brackets this would expand a*
to the list of files whose names starts with a
in the current directory. Double brackets were first introduced in ksh and are only available in ksh, bash and zsh.
Inside single brackets, you need to use double quotes around variable substitutions, like in most other places, because they're just arguments to a command (which happens to be the [
command). Inside double brackets, you don't need double quotes, because the shell doesn't do word splitting or globbing: it's parsing a conditional expression, not a command.
An exception though is [[ $var1 = "$var2" ]]
where you need the quotes if you want to do a byte-to-byte string comparison, otherwise, $var2
would be a pattern to match $var1
against.
One thing you can't do with [[ … ]]
is use a variable as an operator. For example, this is perfectly legal (but rarely useful):
if [ -n "$reverse_sort" ]; then op=-gt; else op=-lt; fi
…
if [ "$x" "$op" "$y" ]; then …
In your example
dir="/home/mazimi/VirtualBox VMs"
if [ -d ${dir} ]; then …
the command inside the if
is [
with the 4 arguments -d
, /home/mazimi/VirtualBox
, VMs
and ]
. The shell parses -d /home/mazimi/VirtualBox
and then doesn't know what to do with VMs
. You would need to prevent word splitting on ${dir}
to get a well-formed command.
Generally speaking, always use double quotes around variable and command substitutions unless you know you want to perform word splitting and globbing on the result. The main places where it's safe not to use the double quotes are:
- in an assignment:
foo=$bar
(but note that you do need the double quotes in export "foo=$bar"
or in array assignments like array=("$a" "$b")
);
- in a
case
statement: case $foo in …
;
- inside double brackets except on the right hand side of the
=
or ==
operator (unless you do want pattern matching): [[ $x = "$y" ]]
.
In all of these, it's correct to use double quotes, so you might as well skip the advanced rules and use the quotes all the time.