#426990 bash: "help test" gives no hint of complicated precedence rules

Package:
bash
Source:
bash
Description:
GNU Bourne Again SHell
Submitter:
OZAKI Masanobu
Date:
2010-11-09 14:03:05 UTC
Severity:
minor
Tags:
#426990#5
Date:
2007-06-01 07:05:11 UTC
From:
To:
Though I have not tested for the stable version, I am afraid this bug
still exists in the latest version because I cannot find any related
bug on the BTS.
Not only Debian (oldstable) but the latest Suse has the same
problem, and I am afraid this is a very widely-spread unfixed bug...

Please try
  % bash -c 'test ! -a . && echo true'
and compare with the result of
  % bash -c '/usr/bin/test ! -a . && echo true'
.
The '[' builtin has the same problem, as expected.

What is interesting is that the results of
  % bash -c 'test ! -z foo && echo true'
and
  % bash -c '/usr/bin/test ! -z foo && echo true'
agree to each other.


Regards,
Masanobu Ozaki

#426990#10
Date:
2010-11-05 08:17:13 UTC
From:
To:
tags 426990 + moreinfo
found 426990 bash/4.1-3
quit

Hi Masanobu,

OZAKI Masanobu wrote:

So which one is right?

My first reading was:

	test ! -a .
=	test ! && test .
=	test -n ! && test -n .
=	true && true
=	true

though I expect you were looking for:

	test ! -a .
=	! test -a .
=	! test -e .
=	! true
=	false

Right, this one is not ambiguous.

My recommendation is to never use the ! or -a 'test' operators.
They are superfluous next to the shell-supported ! and &&.
POSIX does not mandate support for -a (though XSI does), perhaps for
this reason.

Hope that helps,
Jonathan

#426990#19
Date:
2010-11-05 12:10:25 UTC
From:
To:
Hi Jonathan,
sometimes returns the correct result, but have not found what makes the
difference.  Plus, this problem might have been fixed in current stable
(Version: 3.2-4) package: I cannot represent the problem now.

According to the help command, the '-a FILE' file operator returns true
if file exists.
I found this problem when debugging a code written by my colleague.

Thanks,
Masanobu

#426990#24
Date:
2010-11-05 21:24:23 UTC
From:
To:
tags 426990 - moreinfo
tags 426990 + upstream
# documentation bug
severity 426990 minor
retitle 426990 bash: "help test" gives no hint of complicated precedence rules
quit

OZAKI Masanobu wrote:

Summarizing: There are two -a operators: a unary one and a binary one.
This results in reduce/reduce conflicts when parsing a "test" expression.

"help test" says:

	See the bash manual page bash(1) for the handling of
	parameters (i.e., missing parameters).

which does not really tempt me to look at the manual.  bash(1) says:

	Expressions may be combined using the following
	operators, listed in decreasing order of precedence.

giving an order of precedence with ! before -a.  Then it quickly
corrects itself:

	test and [ evaluate conditional expressions using a set
	of rules based on the number of arguments.

		0 arguments
			The expression is false.
[...]
		3 arguments
			If the second argument is one of the
			binary conditional operators listed above
			under CONDITIONAL EXPRESSIONS, the result
			of the expression is the result of the
			binary test using the first and third
			arguments as operands.  The -a and -o
			operators are considered binary operators
			when there are three arguments.

That answers the question.  Unfortunately, the next sentence is
inconsistent with that answer:

							 If the first
			argument is !, the value is the negation of
			the two-argument test using the second and
			third arguments.

Suggested changes:

 1. Change the parenthesis in "help bash" to something like
    "(e.g., operator precedence and missing parameters)".

 2. Change the second paragraph in the description in bash(1) of
    the test builtin to something like

	Expressions may be combined using the following operators.
	The evaluation depends on the number of arguments; see below.
	When 5 or more arguments are present, this list is in
	decreasing order of precedence.

 3. Add the word "Otherwise," before "If the first argument is !" in
    the 3-argument case.

Thoughts?  I can try writing a patch if this looks like a good idea.

#426990#37
Date:
2010-11-08 13:45:11 UTC
From:
To:
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?)
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
[...]

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

#426990#42
Date:
2010-11-08 15:01:47 UTC
From:
To:
You should probably ask the person who added that to Debian's version of
bash.

The phrase you're quoting appears in both the description of the [[ conditional
command and the test/[ builtin.  If you're quoting the latter, why omit the
next sentence, which reads:

The evaluation depends on the number of arguments; see below.

Again, talk to the Debian maintainer who added it.

Something like that is reasonable.

The sentences are supposed to indicate precedence.  It would actually
be clearer if they were separated into bullet points, as they are in
the Posix standard.  I'll look at doing that.

Chet

#426990#47
Date:
2010-11-09 11:29:15 UTC
From:
To:
Hello.

On Mon, 2010-11-08 at 08:45:11 -0500, Greg Wooledge <wooledg@eeg.ccf.org> wrote:
"both should echo true" in previous email, but that is wrong.
Both should echo nothing, as you expect.  I am sorry for my confusing
message.

Neither on my system.  I found descriptions of the -a unary operator
in the output of "help test" output and ksh(1) man page.
According to Solaris 7's test(1) page
(http://docs.sun.com/app/docs/doc/805-3172/6j31br5p3?l=en&a=view),
which summarizes variations of test command, I guess this is ksh origin
and influenced (or polluted?:) /bin/test and bash.  I found the
inconsistency between bash-builtin and GNU shell util's test commands when
porting a ksh script to bash in order to avoid a ksh's bug that randomly
occurred on Scientific Linux.

Regards,
Masanobu

#426990#52
Date:
2010-11-09 13:58:58 UTC
From:
To:
It's difficult to figure out who first introduced an extension.  Most
of these things predate POSIX, and were extensions to either SVID or
BSD standards.  POSIX had the unenviable job of deciding which features
were common enough to mark as "standard" and which were not.

On a different note, that man page is rather dodgy.  It says

  /usr/bin/test [condition]

and then

  if condition ; then action ; fi

Are they saying that Solaris's sh would accept this?

  if -d /etc; then echo "good, you have /etc"; fi

That's what it LOOKS like it's saying, but surely that can't be right....

"It's easier to port a shell than a shell script." -- Larry Wall