bug-bash
[Top][All Lists]
Advanced

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

Re: Possible bug in bash


From: Robert Elz
Subject: Re: Possible bug in bash
Date: Sat, 14 May 2022 17:28:12 +0700

    Date:        Fri, 13 May 2022 23:27:50 -0400
    From:        Greg Wooledge <greg@wooledge.org>
    Message-ID:  <Yn8htkSzZk8NHCiY@wooledge.org>

  | Not really.

What's the difference?

  | Let's say you have a bunch of commands strung together like this:
  |
  | a && b || c && d || e && f || g
  |
  | We start with the shell's command parser pointing to "a", which I'll
  | represent like this:
  |
  | a && b || c && d || e && f || g
  | ^

Or instead
        { { { { { a && b ;} || c ;} && d ;} || e ;} && f ;} || g

We start by executing the LHS of the outer || operator
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
which we execute by executing the LHS of the && operator in that
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
which we execute by executing the LHS of the || operator in that
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
which we execute by executing the LHS of the && operator in that
                 ^^^^^^^^^^^^^^^^^^
which we execute by executing the LHS of the || operator in that
                  ^^^^^^^^^
which we execute by executing the LHS of the && operator in that
                  ^
which is a

  | So, "a" is executed, and this sets the $? special parameter to some value,
  | either 0 or nonzero.

The same for both variants.  Though while $? does get set, I believe it is
more correct to say that the exit status (which $? is defined to fetch) is
set.

  | The parser moves to the right and sees "&& b":

It isn't the parser, this whole thing must have already been parsed before
anything is executed, it is the format that the parser produces (usually a
tree, but it could be executable code for some processor, or pseudo-code for
an interpreter) which is being used here - but this is not much more than a
pedantic detail.

  | a && b || c && d || e && f || g
  |   ^^^^
  |
  | So it checks $?.  If $? is 0, then b will be executed.

In the alternative case, the shell is executing the a && b sub-expression,
and has just executed a - it does exactly the same to do that as you
described.

  | Otherwise, the parser will keep reading to the right.

Again, not the parser...

  | Next it sees "|| c":
  |
  | a && b || c && d || e && f || g

In our case, we now have an exit status from the { a && b; } group.
If a exited with status 0, then the group's status is the status with
which b exited, otherwise it is a's exit status, and b was never evaluated.
(In either case, the group's status is that of the last command executed
in the group, which here is the (short) and-or list, and its status is that
of whichever of its two commands was the final one executed.)

  | If $? is nonzero (either from "a" or from "b"), then "c" will be executed.

So now we can return one step up our parse tree, where we now
have
        { $? || c ;}
which is not (anything like) real code - I am using $? there to indicate
that the sub-expression that was there has already been evaluated, and we
have its exit code.   In this, with the || operator, if $? is non-zero
(either because a exeited non-zero, or it exited 0, and b exited non-zero)
we evaluate the RHS (c), otherwise (both a and b exited 0) we already have
the result (0) and we are done with this sub-expression, without c being
executed.

  | And so on, until the entire line has been processed.

Exactly.

  | Each simple command in the line is either executed, or not,
  | depending on the current value of $? and the operator which precedes it.

The apparent mechanism appears to be slightly different when we have
only binary && and || operators, and groups, but what actually gets
executed is identical.   That's what makes them equivalent.

  | That's why this has no equivalence to a regular "if/then/else" command.
  | The implementation is just entirely different.

The implementation isn't what matters.   They are specified (defined) to
behave differently, that's what matters.

  | Here's an actual example:
  |
  | unicorn:~$ false && true || true && echo a || false && echo b
  | a
  | b

And the same example in the other format:

jinx$ { { { { false && true ;} || true ;} && echo a ;} || false ;} && echo b
a
b

It takes more chars to write that way, but it is much easier to examine
and determine what is going to happen.

  | In real life, there is no reason to write code like this.  It's horrible
  | and confusing.  Just don't do it.

Agreed.    And even in the simple cases, just to reinforce things, don't
write && when what you mean is if.   It isn't cool, it is dangerous.

kre





reply via email to

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