0

I've been using Bash for a long time, but it seems I still miss something. Please consider this code:

function surprise {
    true && {
        echo 'Expected';
        false;
    } || {
        echo 'Unexpected';
    }
}

surprise;

which is shortened demonstration of my real problem and the output:

Expected
Unexpected

Till now I have thought, that whatever "Expected" block returns does not affect || in front of the "Unexpected" block, but it seems, like || is not checked against the output of true command, but in fact of the false command inside brackets.

Can someone, please, explain, what actually just happened? Thank you in advance.

2

it seems, like || is not checked against the output of true command, but in fact of the false command inside brackets.

You could say that's the case.

x && y || z is not an if/then/else structure, nor should be used as one. It is a combination of boolean operators – the equivalent of "x AND y OR z" – and will evaluate as many commands as it needs to in order to determine the result. (The evaluation is left-to-right unless grouped by parentheses or braces, just like in most programming languages, so the order is (x && y) || z.)

So in order to determine the result of x && y, when x is true, y must also be evaluated. On the other hand, when x is false, the result will be false no matter what, so it'll just short-circuit and skip the evaluation of y.

Whether the values in between are single commands or {cmd; cmd; cmd} blocks actually doesn't matter at all; in this situation it merely helps you see the actual effects.

  • The short-circuit... I really thought of && and || more like if-then-else, but now I get it--thanks! – Cromax Jan 29 '19 at 20:12
2

First: you can think of each of the "blocks" as just complex commands, not something significantly different. Thus, what you really have is just:

cmd1 && cmd2 || cmd3

...and the fact that cmd2 and cmd3 are blocks rather than simple commands doesn't matter. Now, the important thing is that given that command sequence, the logical precedence of && and || "operators" makes it something like this:

( cmd1 && cmd2 ) || cmd3

(Note: actually, those parentheses would force a subshell. I'm ignoring that.) If you look at the logical structure of that, it's ( something ) || cmd3, so clearly it's going to run cmd3 if something fails. But something is actually cmd1 && cmd2, which succeeds if *both cmd1 and cmd2 succeed -- which means it fails if either cmd1 or cmd2 fails.

So, logically, that'll wind up running cmd3 if either cmd1 or cmd2 fails. In your actual code, cmd2 always fails, so cmd3 always runs.

| improve this answer | |
0

The double and (&&) and the double pipe (||) have meaning in process control in Linux.

Think of them as logical operators.

command1 && command2

or

command1 || command2

Or

command1 && command2 || command3

Basically, in all 3 cases, command1 runs. If it has a NON-ZERO exit status (an error of some type) it is considered to not have successfully run.

With the && - thinking of a logical AND in programming - there is no need to run the second command, because the first one failed in some way.

With the || - thinking of a logical OR - the if the first is successful (exit 0) there is no need to check the second condition - only one or the other has to be successful.

With the 3rd example, if command1 is successful, it will run command2. If it wasn't successful, it will instead run command3

In your example, the || is detecting the exit status of your false command.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.