sh AND and OR in one command

52,681

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
Share:
52,681

Related videos on Youtube

teracow
Author by

teracow

Updated on September 18, 2022

Comments

  • teracow
    teracow almost 2 years

    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 ? :)

    • Alessio
      Alessio about 8 years
      do you mean A && (B || C) or (A && B) || C? see stackoverflow.com/a/6270803/137158
    • Alessio
      Alessio about 8 years
      in fact, there are several good answers/explanations (many from SE sites) with a simple google search: google.com.au/search?q=bash+boolean+logic
    • Julie Pelletier
      Julie Pelletier about 8 years
      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?
    • bsd
      bsd about 8 years
      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
    • teracow
      teracow about 8 years
      thanks for the quick answers you good people. :) I've updated my question to try to make it clearer.
    • bsd
      bsd about 8 years
    • Alessio
      Alessio about 8 years
      why tag it as /bash when it's a sh question, not a bash question?
    • teracow
      teracow about 8 years
      thanks cas and bsd for that link. I can probably adapt * if [ "$varA" = 1 ] && { [ "$varB" = "t1" ] || [ "$varC" = "t2" ]; }; then * and move the braces.
    • terdon
      terdon about 8 years
      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).
    • teracow
      teracow about 8 years
      (sh) - inside my NAS. And yes "if (A AND B) OR C" is correct.
    • terdon
      terdon about 8 years
      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.
    • teracow
      teracow about 8 years
      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? :)
    • terdon
      terdon about 8 years
      Yes, that's bash running in POSIX compatibility mode. Which means you should be able to use [[ "$string1" != "$string2" ] && "$string3" != $string4" ]] || "$bool1" && echo "continue".
  • teracow
    teracow about 8 years
    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.
  • terdon
    terdon about 8 years
    @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.
  • teracow
    teracow about 8 years
    Thanks 林果皞, your second example looks like where I'm hoping to go with this. I'll test it and post back. :)
  • Alexandros
    Alexandros about 8 years
    > "$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.
  • teracow
    teracow about 8 years
    @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
  • terdon
    terdon about 8 years
    Actually, in this context, && and || have the same precedence. Your first example works because it is executed with left associativity. The && operator has higher precedence than || only in (( )) and [[ ]]. See Precedence of the shell logical operators &&, ||.
  • 林果皞
    林果皞 about 8 years
    @terdon you're right.
  • teracow
    teracow about 8 years
    @林果皞 - the second example works for me. Thanks. :)
  • teracow
    teracow about 8 years
    Thanks Saurav, your last one works for me as well. [[ "$string1" != "$string2" && "$string3" != "$string4" || $bool1 == true ]] && echo "conditions met - running code ..."
  • ilkkachu
    ilkkachu over 3 years
    There's something off with the last two sets of examples, at least { "$bool1" == true ]] || "$bool1" == true ;} seems wrong, it's missing some [[ and ]], but I'm not going to go through them right now.