From man bash:

       -a file
              True if file exists.
       -e file
              True if file exists.
  1. So what is the difference between [ -a $FILE ] and [ -e $FILE ], if any?
  2. If there is no real difference, why do two flags for the same purpose exist?
  • Only the developers would know #2---unless they shared their reasoning to the community. Jul 31 '14 at 18:48

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

-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.

  • Cool to know! Now it's a bit more clear :)!
    – polym
    Jul 31 '14 at 19:30

.1. So what is the difference between [ -a $FILE ] and [ -e $FILE ], if any?

There is no difference at all.

In line 505-507 in test.c of the bash version 4.2.45(1)-release:

case 'a':           /* file exists in the file system? */
case 'e':
  return (sh_stat (arg, &stat_buf) == 0);

That indicates that there is no real difference between both flags.

.2. If there is no real difference, why do two flags for the same purpose exist?

See cuonglm's answer.

  • 3
    On the other hand, given that -a is also a boolean operator (and) in bash, it seems bug prone to add this additional meaning that is completely redundant; I'd guess it is more related to compatibility to some other implementation and/or compatibility with earlier versions that didn't have -e.
    – celtschk
    Jul 31 '14 at 18:37
  • @celtschk - it is not bugprone, but the shell's parser may be. POSIX specifies the -a and -o booleans for [ test ]. But some shells (like bash) sucked at distinguishing an argument from an operator and the [ test ] results were unreliable. Since then POSIX has required that any compliant shell handle at least 4 arguments within the [ test ] brackets.
    – mikeserv
    Aug 1 '14 at 1:24

The best answer I have found is this one, from a StackOverflow question:

-a is deprecated, thus isn't listed in the manpage for /usr/bin/test anymore, but still in the one for bash. Use -e . For single '[', the bash builtin behaves the same as the test bash builtin, which behaves the same as /usr/bin/[ and /usr/bin/test (the one is a symlink to the other). Note the effect of -a depends on its position: If it's at the start, it means file exists. If it's in the middle of two expressions, it means logical and.

[ ! -a /path ] && echo exists doesn't work, as the bash manual points out that -a is considered a binary operator there, and so the above isn't parsed as a negate -a .. but as a if '!' and '/path' is true (non-empty). Thus, your script always outputs "-a" (which actually tests for files), and "! -a" which actually is a binary and here.

For [[, -a isn't used as a binary and anymore (&& is used there), so its unique purpose is to check for a file there (although being deprecated). So, negation actually does what you expect.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.