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