How does AND and OR operators work in Bash? - bash

I tried the following command on bash
echo this || echo that && echo other
This gives the output
this
other
I didn't understand that!
My dry run goes this way :
echo this || echo that && echo other implies true || true && true
Since, && has more precedence than ||, the second expression evaluates first
Since, both are true, the || is evaluated which also gives true.
Hence, I conclude the output to be:
that
other
this
Being from a Java background where && has more precedence than ||, I am not able to relate this to bash.
Any inputs would be very helpful!

From man bash
3.2.3 Lists of Commands
A list is a sequence of one or more pipelines separated by one of the operators ‘;’, ‘&’, ‘&&’, or ‘||’, and optionally terminated by one of ‘;’, ‘&’, or a newline.
Of these list operators, ‘&&’ and ‘||’ have equal precedence, followed by ‘;’ and ‘&’, which have equal precedence.
So, your example
echo this || echo that && echo other
could be read like
(this || that) && other

In bash, && and || have equal precendence and associate to the left. See Section 3.2.3 in the manual for details.
So, your example is parsed as
$ (echo this || echo that) && echo other
And thus only the left-hand side of the or runs, since that succeeds the right-hand side doesn't need to run.

Boolean evaluation in bash is short-circuit: true || false will never evaluate the false operand, because the true operand is enough to determine the outcome of the operation. Likewise, false && true will not evaluate the true operand, because it cannot change the value of the expression.
Boolean evaluation in bash is actually used mainly for controlling the conditional evaluation of the operands, not their order. Typical usage is do_foo || do_bar_if_foo_fails or do_foo && do_bar_only_if_foo_has_succeeded.
In no situation will your echo that command be executed, because the echo this is true and determines the value of the entire echo this || echo that sub-expression.

From man bash:
Of these list operators, && and || have equal precedence, followed by
; and &, which have equal precedence.
So your output is expected.

I think you've already figured it out. Operator precedence doesn't work like that in bash. Everything just goes left to right in your example.

Let's explain what this does:
echo this || echo that && echo other
echo this || echo that -> echo that only if echo this FAILS.
&& echo other -> echo other only if the command before && SUCCEEDS.
So basically:
echo this---> SUCCESS ----> echo that ----> is not executed since echo this succeeded ---> echo other ---> is executed cause echo this || echo that was executed correctly.

Related

Why is empty string changed into -n expression in bash

Taken this snippet:
$ [[ ""=="foo" ]] && echo yes || echo no
+ [[ -n ==foo ]]
+ echo yes
yes
How does [[ ""=="foo" ]] turn into [[ -n ==foo ]] ?
The RC was of course missing spaces around == - after adding them, it works as expected:
$ [[ "" == "foo" ]] && echo yes || echo no
+ [[ '' == \f\o\o ]]
+ echo no
no
But still i cannot understand why it behaved like this?
It's not changing the empty string into -n.
The string ""=="foo" is equivalent to the string ==foo. The trace output always shows strings in their simplest format, without unnecessary quotes.
A conditional expression that just contains a single string with no operators is true if the string is not empty. That's what the -n operator tests, so the -x expansion shows it that way.
Any operand that isn't preceded or followed by an operator is treated to have an equal operation as -n <operand>. Operators also need to be isolated with spaces to be distinguished. For a list of operators run help test. Also run help [[ to see how the keyword is different from the [ and test builtins.

Consider a specific exit code not a failure and proceed

I found questions about ignoring errors in shell scripts, but they ignore any error, not just a specific one.
My use case is that in some cases my_command returns an exit code -3 (253), but that is not a real error and any &&-chained commands should execute. In case of a different exit code != 0, it should stop.
So basically I'm looking for something like
my_command || $(($?==253 ? true : false) && next_command
and I've looked at the different options of using ternary operators, but none of them seem to apply to my case.
What can I do?
There are some problems cmd1 || $(($?==253 ? true : false) && cmd2:
A ) is missing after false.
You don't want $(( ... )) but (( ... )). The former would execute the result of the expression (that is a number!) as a command. The latter just evaluates the expression and fails if the result is 0 and succeeds otherwise. Note that is the opposite of how exit codes work.
true and false are not commands here, but variables. If an undefined variable is used inside (( ... )) its value is always 0. Therefore the command ((... ? true : false)) always fails.
Here is want you could have written instead:
cmd1 || (($?==253 ? 1 : 0)) && cmd2
Test:
prompt$ true || (($?==253 ? 1 : 0)) && echo ok
ok
prompt$ false || (($?==253 ? 1 : 0)) && echo ok
prompt$ ( exit 253; ) || (($?==253 ? 1 : 0)) && echo ok
ok
However, the ternary operator isn't really needed here. The following would be equivalent:
cmd1 || (($?==253)) && cmd2
Despite that, an if would probably be better. If you don't want to use one inline, you can write a function for that:
# allow all exit codes given as the first argument
# multiple exit codes can be comma separated
allow() {
ok=",$1,"
shift
"$#"
[[ "$ok" = *,$?,* ]]
}
allow 0,253 cmd1 && cmd2
Or define a wrapper just for this one command
cmd1() {
command cmd1 "$#" || (( $? == 253 ))
}
cmd1 && cmd2
The code you have evaluates to a number (once you fix the missing closing parenthesis), which is not what you want; but you can refactor it slightly if you want to maintain the same sort of flow, at the expense of a subshell.
my_command || ( exit $(($?==253)) ) && next_command
Probably a less expensive and, all things counted, both more obvious and elegant solution is the trivial
my_command || [ $? == 253 ] && next_command
Um, like this?
my_command; (($?==0|$?==253)) && next_command

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?
The overall general rule is: any expression has the exit code of the last command executed. [*]
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).
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.

Compound if / logical XOR bash bug?

#!/bin/bash
function logic_test()
{
left_bracket=$1
right_bracket=$2
if [[ ($left_bracket || $right_bracket) && ! ($left_bracket && $right_bracket) ]]
then
errEcho "Input error: insertIntoConfigFile arg1 does not contain matching []."
else
errEcho "Passed"
fi
}
logic_test true true
logic_test true false
logic_test false true
logic_test false false
Expected results, as per XOR's functioning:
Passed
Input Error
Input Error
Passed
Actual results in 4.3.11(1)-release
Passed
Passed
Passed
Passed
Am I failing to see something trivial in my XOR implementation, or is the if logic buggy? If it is a bug, how do I go about submitting said bug?
I'm thinking this might be a bug...if I change the if to
if [[ ($left_bracket && !$right_bracket) || (!$left_bracket && $right_bracket) ]]
then all 4 outputs become "input error"
You can use set -xv to turn on debugging. It will show you the problem: bash interprets the things in [[ .. ]] as strings and tests them with -n. None of them is emtpy, so it works as 'true true' everytime.
To fix it, just remove the [[ and ]]. Bash will correctly interpret true and false as commands to run and use their exit code in the logic.

How does || and && return value notations work?

Many use the notation below in scripting languages, but I don't quite understand how it works.
~$ false || echo "aa"
aa
~$ true || echo "aa"
~$ true && echo "aa"
aa
~$ false && echo "aa"
~$
Are both sides executed first, and then their return values evaluated?
What would be a more straight forward way to write these?
Is || an XOR?
Using && and || to control the evaluation is a very fundamental thing in
programming. As many already answered, they are using "Short Circuit Evaluation".
In some languages they are the same as and and or, but not in all. In e.g.
Ruby they do not mean exactly the same (as I know).
What would be a more straight forward way to write these?
You could say that using && and || instead of if ... else is lazy
programming, but some people would say that using && and || structure is
the most straight forward.
If you will have an alternative (or a way of understand this better):
expr && echo "aa"
# is the same as
if expr; then
echo "aa"
fi
and
expr || echo "aa"
# is the same as
if ! expr; then
echo "aa"
fi
and
expr && echo "yes" || echo "no"
# is the same as
if expr; then
echo "yes"
else
echo "no"
fi
and of course a mix
if this && that; then
echo "yes"
else
echo "no"
fi
The "Short Circuit Evaluation" is a very important rule, and in some
situations the code is depending on this.
In this example (from VIM, but it's the same in many languages) there would be an exception if it doesn't work:
if exists("variable") && variable == 3 ...
If both sides was evaluated before deciding the result there would be an
exception if variable isn't defined.
There have been some bad designed languages where both sides of e.g. && was
evaluated before the result was given, they were not so fun to use.
So:
if you like it; then
use it && be happy
else
use it || be happy
fi
This is called Short Circuit Evaluation.
|| is an OR (not an XOR).
With scripting (as with source code) the right-side is only evaluated if necessary.
with || the RHS is evaluated if the LHS evaluates to false or 0. With && the RHS evaluates only if the LHS evaluates to true.
That is because true || anything is always true so there is no need to evaluate the RHS. And false && anything is always false.
See also Benefits of using short-circuit evaluation
|| and && are lazy in all the languages you listed.
The way it works is that the expression is evaluated left to right. In the case of ||, the value of the entire expression is the value of first argument which is not "false-y" (or the last argument, if all of the arguments are false-y) will be the value of the expression.
I think this is pretty simple.
The left side is evaluated first every time and the right side is evaluated if required.
In the first case, LHS is false, so the result of the expression depends on the RHS as it is an OR operation. So the right side is evaluated/executed.
In the second case, LHS is true, and the result of the OR operation will be true regardless of what is there in the RHS, so the RHS is never evaluated/executed.
Same thing happens with the AND operation. It needs to evaluate the RHS only if LHS is true as in the 3rd statement.

Resources