Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

From man bash:

CONDITIONAL EXPRESSIONS
[...]
       -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?
share|improve this question
    
Only the developers would know #2---unless they shared their reasoning to the community. –  Belmin Fernandez Jul 31 at 18:48

3 Answers 3

up vote 11 down vote accepted

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.

share|improve this answer
    
Cool to know! Now it's a bit more clear :)! –  polym Jul 31 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 gnouc's answer.

share|improve this answer
2  
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 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 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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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