If you want to stick with [ ... ]
, use (quoted) parentheses to disambiguate[1]
.
if [ ! \( -a bar \) ]; then echo "Yes"; fi
If you're writing a bash
script (without needing to worry about portability), use [[ ... ]]
, which obviates the need for parentheses:
if [[ ! -a bar ]]; then echo "Yes"; fi
[1] Barmar, in a since deleted answer, explained the ambiguity inherent in [ ! -a bar ]
:
-a
serves both as a unary file-existence operator and as the binary logical AND operator.
In the case at hand, -a
, due to the presence of 3 arguments, is interpreted as the logical operator, treating !
and bar
as its literal operands: both !
and bar
are non-empty strings, which are considered truthy in a Boolean context, so the overall expression is always true.
By contrast, bash
's [[ ... ]]
construct exclusively uses &&
for logical AND (and ||
for OR) to avoid this ambiguity; in other words: -a
is only recognized as the unary file-existence operator, so there's no ambiguity, and parentheses aren't needed.