In bash
, with context of two arguments test
command, -a file
and -e file
are the same. But they have some difference, because -a
is also a binary operator.
-e
unary is defined by POSIX, but -a
unary isn't. POSIX only defines -a
binary (See test POSIX).
POSIX defines three arguments test
behaviour:
3 arguments:
If $2 is a binary primary, perform the binary test of $1
and $3.
If $1 is '!', negate the two-argument test of $2 and $3.
If $1 is '(' and $3 is ')', perform the unary
test of $2. On systems that do not support the XSI option, the results are unspecified if $1 is '(' and $3 is ')'.
Otherwise, produce unspecified results.
So -a
also leads to strange result:
$ [ ! -a . ] && echo true
true
-a
is considered as binary operator in context of three arguments. See Bash FAQ question E1.
POSIX also mentions that -a
is get from KornShell but was changed later to -e
because it makes confusing between -a
binary and -a
unary.
The -e primary, possessing similar functionality to that provided by
the C shell, was added because it provides the only way for a shell
script to find out if a file exists without trying to open the file.
Since implementations are allowed to add additional file types, a
portable script cannot use:
test -b foo -o -c foo -o -d foo -o -f foo -o -p foo
to find out if foo is an existing file. On historical BSD systems, the
existence of a file could be determined by:
test -f foo -o -d foo
but there was no easy way to determine that an existing file was a
regular file. An early proposal used the KornShell -a primary (with
the same meaning), but this was changed to -e because there were
concerns about the high probability of humans confusing the -a primary
with the -a binary operator.
-a
binary is also marked as obsolescent, because it leads to some ambiguous expression, which has greater than 4 arguments. With these >4 arguments expression, POSIX defines the result is unspecified.
Best Answer
The difference between
[[ … ]]
and[ … ]
is mostly covered in Why does parameter expansion with spaces without quotes work inside double brackets "[[" but not inside single brackets "["?. Crucially,[[ … ]]
is special syntax, whereas[
is a funny-looking name for a command.[[ … ]]
has special syntax rules for what's inside,[ … ]
doesn't.With the added wrinkle of a wildcard, here's how
[[ $a == z* ]]
is evaluated:[[ … ]]
conditional construct around the conditional expression$a == z*
.==
binary operator, with the operands$a
andz*
.a
.==
operator: test if the value of the variablea
matches the patternz*
.Here's how
[ $a == z* ]
is evaluated:[
command with the arguments formed by evaluating the words$a
,==
,z*
,]
.$a
into the value of the variablea
.a
is the 6-character stringfoo b*
(obtained by e.g.a='foo b*'
) and the list of files in the current directory is (bar
,baz
,qux
,zim
,zum
), then the result of the expansion is the following list of words:[
,foo
,bar
,baz
,==
,zim
,zum
,]
.[
with the parameters obtained in the previous step.[
command complains of a syntax error and returns the status 2.Note: In
[[ $a == z* ]]
, at step 3, the value ofa
does not undergo word splitting and filename generation, because it's in a context where a single word is expected (the left-hand argument of the conditional operator==
). In most cases, if a single word makes sense at that position then variable expansion behaves like it does in double quotes. However, there's an exception to that rule: in[[ abc == $a ]]
, if the value ofa
contains wildcards, thenabc
is matched against the wildcard pattern. For example, if the value ofa
isa*
then[[ abc == $a ]]
is true (because the wildcard*
coming from the unquoted expansion of$a
matchesbc
) whereas[[ abc == "$a" ]]
is false (because the ordinary character*
coming from the quoted expansion of$a
does not matchbc
). Inside[[ … ]]
, double quotes do not make a difference, except on the right-hand side of the string matching operators (=
,==
,!=
and=~
).