The bash man page says that the unary -a behaves like -e:
-a file
True if file exists.
-e file
True if file exists.
But I get:
vlefevre@vin:~$ touch exists
vlefevre@vin:~$ [ ! -e exists ] || echo found
found
vlefevre@vin:~$ [ ! -a exists ] || echo found
vlefevre@vin:~$
However the following forms work as expected:
vlefevre@vin:~$ [[ ! -e exists ]] || echo found
found
vlefevre@vin:~$ [[ ! -a exists ]] || echo found
found
vlefevre@vin:~$ [ -e exists ] && echo found
found
vlefevre@vin:~$ [ -a exists ] && echo found
found
vlefevre@vin:~$
Note: I don't think it is a good idea to use the non-standard unary -a
with [, but this is a bug anyway.
In fact, this seems to be the intended behavior, but the man page
is not well structured. It first says:
! expr True if expr is false.
but then, says something about the number of araguments. This part
should appear first. Moreover, the case "! -a file" is not handled
in the description:
3 arguments
If the second argument is one of the binary conditional
operators listed above under CONDITIONAL EXPRESSIONS, the
As the first argument is !, the -a is a unary conditional operator
as described above, so we are not in this case.
result of the expression is the result of the binary test
using the first and third arguments as operands. If the
first argument is !, the value is the negation of the
two-argument test using the second and third arguments.
[...]
We are in this case.
If the first argument is exactly ( and the third argument
is exactly ), the result is the one-argument test of the
second argument. Otherwise, the expression is false.
The -a and -o operators are considered binary operators
in this case.
But now, "in this case" is ambiguous. The man page should probably
have been said "when there are 3 arguments". And it should have been
said first. And above, instead of saying "If the second argument is
one of the binary conditional operators listed above under CONDITIONAL
EXPRESSIONS" (which is ambiguous, as seen), the man page should have
said "If the second argument is -a or -o".
Also, the case with 5 or more arguments is not clear either.
In fact, I don't know if this is a bug (due to a primitive parser)
or a bad documentation.
vlefevre@vin:~$ [ true -a \( ! -a \) ] && echo OK
bash: [: `)' expected, found ]
It is not clear whether the second -a is
1. -a <file> where <file> is ')';
2. -a <file> where <file> is a missing parameter[*], ')' being
part of the ( expr ) form;
3. the string '-a' (assuming there are no parameters left, see
point 2).
[*] The man page says:
See the description of the test builtin command (section SHELL
BUILTIN COMMANDS below) for the handling of parameters (i.e.
missing parameters).
But nothing is described in the test builtin concerning missing
parameters.
The parser seems to follow point 1. But a grammar-based parser would
follow point 2 or 3.
which case is specified by POSIX:
vlefevre@vin:~$ [ \( ! -e \) ] && echo OK
bash: [: `)' expected, found ]
POSIX says: "However, when using the "[...]" form, the right-bracket
final argument shall not be counted in this algorithm."
So, we are in the 4-argument case: ( ! -e )
POSIX says in this case: "If $1 is '(' and $4 is ')', perform the
two-argument test of $2 and $3."
i.e. this is equivalent to: test ! -e
POSIX says in this 2-argument case: "If $1 is '!', exit true if $2 is
null, false if $2 is not null."
So, that's perfectly valid, and the error returned by bash is a bug.
retitle 421591 bash: test builtin (or [) does not behave correctly thanks As a summary, the following test at least does not behave correctly: vlefevre@vin:~$ test \( ! -e \) ; echo $? bash: test: `)' expected 2 And I think this one is also buggy: vlefevre@vin:~$ test true -a \( ! -a \) ; echo $? bash: test: `)' expected 2 FYI, the coreutils and zsh test utility both output 1 on these tests; so does dash on the second one only. I recall the POSIX spec: http://www.opengroup.org/onlinepubs/009695399/utilities/test.html
found 421591 5.2.21-2
thanks
In bash 5.2.21-2 the first invocation behaves correctly:
$ test \( ! -e \) ; echo $?
1
but the second is still broken:
test true -a \( ! -a \) ; echo $?
-bash: test: `)' expected
2
dash now gives 1 on both cases, as expected. But for the second one, zsh now gives an error and 2 like bash; I would see this as a bug, like for bash. Note that $ test \( ! -a \) ; echo $? 1 for both bash and zsh, so "test true -a \( ! -a \)" should really behave in the same way.