13

Trying to check for 3 conditions in one line of code, but I'm stuck.

Essentially, I need to code for the following:

IF

string1 is not equal to string2

AND

string3 is not equal to string4

OR

bool1 = true

THEN

display "conditions met - running code ...".

As requested in the comments, I've updated my example to try to make the problem clearer.

#!/bin/sh

string1="a" string2="b" string3="c" string4="d" bool1=true

the easy-to-read way ....

if [ "$string1" != "$string2" ] && [ "$string3" != "$string4" ] ; then echo "conditions met - running code ..." fi

if $bool1 ; then echo "conditions met - running code ..." fi

or the shorter way ...

[ "$string1" != "$string2" ] && [ "$string3" != "$string4" ] && echo "conditions met - running code ..."

$bool1 && echo "conditions met - running code ..."

The code above will potentially run twice: if the first 2 conditions are met, and then again if the 3rd condition is met. This is not what I need.

The issue with this example is that it involves 2 distinct calls to 'echo' - (note: in the real code, it's not an echo, but you get the idea). I'm trying to reduce the code duplication by combining the 3 condition check into a single command.

I'm sure there's a few people now shaking their heads and shouting at the screen "That's NOT how you do it!"

And there's probably others waiting to mark this as a duplicate ... well, I looked but I'm damned if I could figure out how to do this from the answers I've read.

Can someone please enlighten me ? :)

teracow
  • 352
  • 2
    do you mean A && (B || C) or (A && B) || C? see http://stackoverflow.com/a/6270803/137158 – cas May 05 '16 at 07:55
  • in fact, there are several good answers/explanations (many from SE sites) with a simple google search: https://www.google.com.au/search?q=bash+boolean+logic – cas May 05 '16 at 08:02
  • I'm not sure what your problem is because it seems to do what you asked it to do. What's wrong according to you? – Julie Pelletier May 05 '16 at 08:05
  • Fix your example to either compare constant values or set the variables before the test. Then explain your results and expected results and how they differ – bsd May 05 '16 at 08:26
  • thanks for the quick answers you good people. :) I've updated my question to try to make it clearer. – teracow May 05 '16 at 09:14
  • Also, please note that this question is for 'sh' not 'bash'. :) – teracow May 05 '16 at 09:21
  • why tag it as /bash when it's a sh question, not a bash question? – cas May 05 '16 at 09:26
  • thanks cas and bsd for that link. I can probably adapt * if [ "$varA" = 1 ] && { [ "$varB" = "t1" ] || [ "$varC" = "t2" ]; }; then * and move the braces. – teracow May 05 '16 at 09:26
  • yes, sorry - used to typing bash - my bad. :) – teracow May 05 '16 at 09:28
  • Could you clarify that you want if (A AND B) OR C? I'm still not 100% sure that's what you need. Also, what system will this be running on? Do you really mean bourne shell (sh), or is that bash called as sh (which is the default on many systems) or dash called sh (which is the default on Ubuntu and perhaps others). – terdon May 05 '16 at 09:28
  • (sh) - inside my NAS. And yes "if (A AND B) OR C" is correct. – teracow May 05 '16 at 09:36
  • OK, if it's on your NAS, that's probably going to be busybox sh, yet another one. Please [edit] your question and make that clear. – terdon May 05 '16 at 09:37
  • lol... just been checking in the NAS.... found a symlink from bash to sh... then when you run 'sh -version', it says "GNU bash, version 3.2.57(3)-release (i686-pc-linux-gnu) Copyright (C) 2007 Free Software Foundation, Inc."... So bash it is? :) – teracow May 05 '16 at 09:41
  • Yes, that's bash running in POSIX compatibility mode. Which means you should be able to use [[ "$string1" != "$string2" ] && "$string3" != $string4" ]] || "$bool1" && echo "continue". – terdon May 05 '16 at 09:54

2 Answers2

17

This will work:

if    [ "$string1" != "$string2" ] \
   && [ "$string3" != "$string4" ] \
   || [ "$bool1" = true ]; then
    echo "conditions met - running code ...";
fi;

Or surround with { ;} for readability and easy to maintain in future.

if { [ "$string1" != "$string2" ] && [ "$string3" != "$string4" ] ;} \
   || [ "$bool1" = true ] ; then
    echo "conditions met - running code ...";
fi;

Points to note:

  1. There is no such thing as a boolean variable..
  2. Braces need the final semicolon ({ somecmd;}).
  3. && and || evaluate left-to-right in the above — && has higher precedence than || only within (( )) and [[..]]

&& higher precedence only happen in [[ ]] is proven as follows. Assume bool1=true.

With [[ ]] :

bool1=true
if [[ "$bool1" == true || "$bool1" == true && "$bool1" != true ]]; then echo 7; fi #1 # Print 7, due to && higher precedence than ||
if [[ "$bool1" == true ]] || { [[ "$bool1" == true && "$bool1" != true ]] ;}; then echo 7; fi # Same as #1
if { [[ "$bool1" == true || "$bool1" == true ]] ;} && [[ "$bool1" != true ]] ; then echo 7; fi # NOT same as #1
if [[ "$bool1" != true && "$bool1" == true || "$bool1" == true ]]; then echo 7; fi # Same result as #1, proved that #1 not caused by right-to-left factor, or else will not print 7 here

With [ ] :

bool1=true
if [ "$bool1" == true ] || [ "$bool1" == true ] && [ "$bool1" != true ]; then echo 7; fi #1, no output, due to && IS NOT higher precedence than ||
if [ "$bool1" == true ] || { [ "$bool1" == true ] && [ "$bool1" != true ] ;}; then echo 7; fi # NOT same as #1
if { [ "$bool1" == true ] || [ "$bool1" == true ] ;} && [ "$bool1" != true ]; then echo 7; fi # Same as #1
if [ "$bool1" != true ] && [ "$bool1" == true ] || [ "$bool1" == true ]; then echo 7; fi # Proved that #1 not caused by || higher precedence than &&, or else will not print 7 here, instead #1 is only left-to-right evaluation
林果皞
  • 5,156
  • 3
  • 33
  • 46
0

The && that you are using at the end of your conditional expression is meant for running a subsequent command (its second argument) if the preceding command (its first argument) returns a status of zero, which is traditionally considered a success code for programs. Also, the $bool1 that you have put there is standing alone, which makes Bash interpret its value as the name of a command or program to run. What you are trying to achieve seems to be a conditional expression evaluation, which is done using the [ command as follows:

if [ "$string1" != "$string2" ]  && [ "$string3" != "$string4" ] || [ $bool1 -ne 0 ]; then echo "conditions met - running code ..."; fi

You may see older code using the built-in "and", -a, and "or", -o, operators, but this has been deprecated by the POSIX specification and is not well-defined.

if [ "$string1" != "$string2" -a "$string3" != "$string4" -o $bool1 -ne 0 ]; then echo "conditions met - running code ..."; fi

If you are using a shell derived from ksh (such as bash) you can use the built-in test operator [[:

[[ "$string1" != "$string2" && "$string3" != "$string4" || $bool1 -ne 0 ]] && echo "conditions met - running code ..."

Note that the Boolean value is expressed as an integer, so the integer expression test for "not equal to", -ne, can be used here. The following is a script that demonstrates the working:

#!/bin/bash

string1="Hello" string2="Hello" string3="Hello" string4="Hello" bool1=1

if [ "$string1" != "$string2" ] && [ "$string3" != "$string4" ] || [ $bool1 -ne 0 ]; then echo "conditions met - running code ..."; fi

exit 0

miken32
  • 466
  • Sorry - the title for my question should say 'sh' not 'bash' ... $bool1 "on it's own" is tested and if true, the subsequent command is then run. Not unusual to see and I've been using that method for years. $bool1 as in "boolean" i.e. true or false. Thanks. – teracow May 05 '16 at 09:21
  • 1
    @teracow note that if you set foo=true and then run $foo || command, that isn't checking that the variable is set to true, it runs the shell builtin command "true" (same goes for false). So it isn't actually, strictly speaking, a boolean. – terdon May 05 '16 at 09:36
  • "$bool1 on its own is tested and if true, the subsequent command is then run"

    $bool1 && xyz will interpret $bool1 as a command or program, and, if the evaluation or return value is zero, it will then run xyz. This is not actually a test in the manner of the shell built-in test ([]) function.

    –  May 05 '16 at 09:44
  • @terdon - agreed - I name them according to how I think of them. :) However, I usually only use them for flags for debugging, etc.. as in $debug && echo "this variable is currently: $var"... I don't usually use || in that sense – teracow May 05 '16 at 09:46
  • Going by your statement of "testing" or evaluating $bool1 and combining it with &&, you could also use the following: [[ "$string1" != "$string2" && "$string3" != "$string4" || $bool1 == true ]] && echo "conditions met - running code ...". You can use parentheses to override precedence within the [[...]] block. –  May 05 '16 at 10:07
  • Thanks Saurav, your last one works for me as well. [[ "$string1" != "$string2" && "$string3" != "$string4" || $bool1 == true ]] && echo "conditions met - running code ..." – teracow May 05 '16 at 20:09