bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: 'test' builtin of bash returns wrong value for '! -a FILE' operation


From: Greg Wooledge
Subject: Re: 'test' builtin of bash returns wrong value for '! -a FILE' operation
Date: Mon, 8 Nov 2010 08:45:11 -0500
User-agent: Mutt/1.4.2.3i

On Fri, Nov 05, 2010 at 04:24:23PM -0500, Jonathan Nieder wrote:
> >>> Please try
> >>>   % bash -c 'test ! -a . && echo true'
> >>> and compare with the result of
> >>>   % bash -c '/usr/bin/test ! -a . && echo true'

imadev:~$ bash -c 'test ! -a . && echo true'
true
imadev:~$ bash -c '/usr/bin/test ! -a . && echo true'
imadev:~$ 

This is an extremely convoluted example.  You're echoing true if the
object does NOT exist...?  Actually, you're echoing true if the command
fails to fail.  In the /usr/bin/test -a case, the command has the right to
fail because there's no such unary operator (see below), regardless of
whether . exists in the file system.

(On my system, there is no -a unary operator in the test(1) man page, but
the command apparently supports one, undocumented.  Isn't this fun?)

> >> So which one is right?
> >
> > Both should echo "true", but the former did not: I found that the former
> > sometimes returns the correct result, but have not found what makes the
> > difference.

POSIX does not specify -a as a unary operator.  See
http://www.opengroup.org/onlinepubs/9699919799/utilities/test.html
for details.  It specifies -a as a binary operator, but marks it as
obsolescent and suggests using two test commands instead.  This is the
same advice I give whenever the subject comes up on IRC, and also what
I mention on my pitfalls page: http://mywiki.wooledge.org/BashPitfalls#pf6

> Suggested changes:
[...]

I'll let Chet address those.  Speaking strictly as someone who supports
script writers (not shell maintainers), I'd suggest either using bash's [[
command instead of test, or avoiding the use of -a and -o in the test
command.  (Note that [[ uses && rather than -a, and || rather than -o.
So whichever advice path you choose, -a and -o as binary operators are
just plain bad.)

If you're trying to determine whether some file-object exists, use -e.

imadev:~$ bash -c 'test ! -e . && echo true'
imadev:~$ bash -c '/usr/bin/test ! -e . && echo true'
imadev:~$ 

If you're trying to run multiple tests, use multiple test commands,
and string them together with && and/or || as required.

imadev:~$ bash -c 'if test -e . && test -e ..; then echo true; fi'
true
imadev:~$ bash -c 'if /usr/bin/test -e . && /usr/bin/test -e ..; then echo 
true; fi'
true

Or use [[:

imadev:~$ bash -c 'if [[ -e . && -e .. ]]; then echo true; fi'
true



reply via email to

[Prev in Thread] Current Thread [Next in Thread]