cimentadaj
cimentadaj

Reputation: 1488

Order of execution of && and || in Bash

I'm working through some basic exercises using Bash and I'm confused on the order of operations of && and ||. Below are some reproducible examples.

# Example 1
true && false || echo pass
# pass

Since the first true is executed, && passes on to false and false is executed (true && false). || evaluates false and since there's a false on the left hand side, echo pass gets executed (false || echo pass). So far so good.

Example 2

false && false || echo pass
# pass

Since the first expression is false, && does not execute the second false. However, echo pass gets printed because the left hand side of false || echo pass is false. All is good so far.

Example 3

[[ 2 -gt 3 ]] && echo t || echo f
# f

2 is not greater than 3, meaning that echo t doesn't get executed. However, echo t || echo f prints f. Based on the previous two examples, echo t should return a non-exit code and don't execute echo f on the right hand side.

What am I missing?

Upvotes: 3

Views: 4625

Answers (2)

Andrew Vickers
Andrew Vickers

Reputation: 2654

The '&&' and '||' operators do not always execute the second operand. The shell will not execute the second operand if the result of the whole expression.

When evaluating 'cmd1 && cmd2', if 'cmd1' fails, 'cmd2' is not executed and the result is a failure. Otherwise, the result is the result of executing 'cmd2'.

Similarly, when evaluating 'cmd1 || cmd2', if 'cmd1' succeeds, 'cmd2' is not executed and the result is success. Otherwise, the result is the result of executing 'cmd2'.

When multiple operations are chained together, start with the left most pair and evaluate them according to the above two rules. Then replace the left most pair with the result and repeat. For example:

To run multiple commands, but stop and return an error upon the first failure:

cmd1 && cmd2 && cmd3 || echo "Failed."

This is equivalent to

( ( cmd1 && cmd2 ) && cmd3 ) || echo "Failed."

If cmd1 fails, cmd2 is not executed the first pair of commands fails. Therefore cmd3 is not executed and the left hand side of the '||' operator is a failure. Which means the echo command has to be executed.

Alternatively, if cmd1, cmd2 and cmd3 all succeed, then the left hand side of the '||' operator is successful and so the echo command is not executed.

Upvotes: -1

KamilCuk
KamilCuk

Reputation: 141000

  1. The overall general rule is: any expression has the exit code of the last command executed. [*]
  2. Grouping. a && b || c is equal to ( a && b ) || c, ie. the left side is one big expression. && and || have equal precedence, they are executed from left to right.

The last command executed in [[ 2 -gt 3 ]] && echo t is [[ 2 -gt 3 ]] and it returns nonzero. So the exit status of the whole [[ 2 -gt 3 ]] && echo t expression is nonzero - the exit status of last command executed.

[[ 2 -gt 3 ]] && echo t || echo f
( [[ 2 -gt 3 ]] && echo t ) || echo f
( false && echo t ) || echo f
( false ) || echo f
echo f

[*] - The rule is for any command that is in a list of commands ( ) { } && || and also in compound constructs while if case etc. You can do funny stuff like if case "$line" in a) false; ;; esac; then echo "line is not a"; fi. The exit status of case is equal the exit status of the last command executed, which is false in case line matches a).

Upvotes: 6

Related Questions