[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