0

I have just had unexpected result from writing the alternative form of if/then/else blocks.

> #this is the behaviour I expect:  
> if [[ 1 -eq 1 ]]; then echo "good"; false; else   echo "bad"; fi
good

> #assuming no error in the first block these brackets behave okay as well:
> [[ 1 -eq 1 ]] && { echo "good"; } || { echo "bad"; }
good

> #this however allows the process to jump into the first AND second block
> [[ 1 -eq 1 ]] && { echo "good"; false; } || { echo "bad"; }
good
bad

Why does the curly brace method pass the process to the second block. I'm aware there's some statements that there must be a ; before the end of the block, but if bash blocks behave badly on an error condition as a last statement these blocks seem "unsafe" to use.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
rupert160
  • 1,441
  • 1
  • 17
  • 19
  • 1
    `foo && bar || baz` is perfectly safe, as long as you don't try to use them as something they aren't. They aren't `if; then; else`. – Charles Duffy Jun 20 '19 at 13:03
  • 1
    This is why http://shellcheck.net/ flags `foo && bar || baz` with the warning [SC2015](https://github.com/koalaman/shellcheck/wiki/SC2015), to dissuade people who are using it without knowing what it actually does. – Charles Duffy Jun 20 '19 at 13:04
  • (What does this have to do with code blocks? It's the same for *any* command; no matter if it's a single command that echos something and then returns false, or separate `echo` and `false` commands in a block). – Charles Duffy Jun 20 '19 at 13:07

2 Answers2

2

Because the function of || is to execute its right hand side if its left hand side returns nonzero, which false does.

&&/|| is not an if/else, nor is it a ?: trinary operator - it's a chained pair of separate operations.

Paul Hodges
  • 13,382
  • 1
  • 17
  • 36
1

The && and || operators in the shell work with the return value (exit code) of each statement. In a compound statement within { }, the exit status of the last command is the important one.

[[ 1 -eq 1 ]] && { echo "good"; } || { echo "bad"; }

is equivalent to:

true && true || true

So true && true evaluates to true, and the shell "short-circuits" (i.e. doesn't evaluate) the { echo "bad"; } because true || anything is true.

[[ 1 -eq 1 ]] && { echo "good"; false; } || { echo "bad"; }

is equivalent to:

true && false || true

In this case, true && false evaluates to false, and the shell has to evaluate full expression.

The if/else code takes the first branch, because the exit code of [[ 1 -eq 1 ]] is 0 (the same as true in the shell).

Tom Fenech
  • 72,334
  • 12
  • 107
  • 141