Date: Fri, 13 May 2022 23:27:50 -0400 From: Greg Wooledge <g...@wooledge.org> Message-ID: <yn8htkszzk8nh...@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