-a is both a unary (for
accessible, added for compatibility with the Korn shell, but otherwise non-standard and now redundant with
-e) and binary (for
and, in POSIX (with XSI) but deprecated there) operator.
[ ! -a /bin/grep ] invokes the binary operator as required by POSIX. It's
[ "$a" -a "$b" ] to test whether
$a is non-empty and
$b is non empty, here with
/bin/grep. As both strings are non-empty, it returns true.
See also the "The -a and -o operators are considered binary operators when there are three arguments" in the text you quoted.
-a is deprecated in both the unary and binary form, the unary one because it's superseded by
-e, the binary one because it makes for unreliable and ambiguous test expressions.
To test for file existence (though in effect, it's more a test whether the file is accessible, whether
stat() would succeed on the path¹), use
[ -e filepath ]. To and two conditions, use
&& between two invocations of
To test whether a string is non empty, I personally prefer the
[ -n "$string" ] form over the
[ "$string" ] one.
test for file existence:
[ -e "$file" ] # not [ -a "$file" ]
[ ! -e "$file" ] # not [ ! -a "$file" ]
test for two strings being non-empty:
[ -n "$a" ] && [ -n "$b" ] # not [ "$a" -a "$b" ]
[ "$a" ] && [ "$b" ]
From the rationale in the POSIX specification for the
The XSI extensions specifying the -a and -o binary primaries and the '(' and ')' operators have been marked obsolescent. (Many expressions
using them are ambiguously defined by the grammar depending on the specific expressions being evaluated.) Scripts using these expressions
should be converted to the forms given below. Even though many implementations will continue to support these obsolescent forms, scripts
should be extremely careful when dealing with user-supplied input that could be confused with these and other primaries and operators.
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.
The manuals of
coreutils do guard against using binary
-o in their respective
test implementations and
zsh's manual never documented them², but many others including
bash (the GNU shell) unfortunately still don't discourage their usage nor deprecate them.
¹ more on that in this answer of mine to a related stackoverflow Q&A
[ builtin was only added to zsh in version 2.0.3 in 1991. The
[[ ... ]] special construct, from the Korn shell was always preferred there, and has its own syntax where
|| are used for and and or operators.