[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