0

This question is different from the multitude of potential duplicates, and I've not been able to find this particular question answered or even asked...

Maybe I'm overlooking some simple logic here, but I'm running into the following issue:

I'm trying to have an if statement where conditions must be met like $V1 AND $V2 are TRUE || OR $V3 AND $V4 are TRUE. Here's a simple test:

#!/bin/bash

V1="File Placeholder"
#echo $V1
V2="May contain some text"
#echo $V2

V3="Some command output"
#echo $V3
V4="Command output contains this text"
#echo $V4

if [[ "$V1" ]] && [[ "$V2" == *"contain some"* ]] || [[ "$V3" ]] && [[ "$V4" == *"output contains"* ]]
then
echo "Hello $V1"
echo "World full of: $V2"
fi 

Meaning, I'd like to do something if:

$V1 is true (a file exists) AND $V2 is true (some string is found)

OR

$V3 is true (i.e., not null) AND $V4 is true (command output contains text)

It appears to work a bit, but I realize it's not working properly: it won't return TRUE if both the second && conditions are FALSE ie: || [[ "$V6" ]] && [[ "$V4" == *"output contains"* ]] (why I think I may be overlooking some logic, maybe getting cancelled out somehow?).

Why isn't this working as I assume it would if a AND b are TRUE ... OR ... if x AND z are TRUE?

tRuEsAtM
  • 3,517
  • 6
  • 43
  • 83
TryTryAgain
  • 7,632
  • 11
  • 46
  • 82
  • 1
    maybe it helps http://stackoverflow.com/a/14965071/2442831 – Lino Jun 28 '16 at 18:25
  • 1
    Thanks! The third example "order of evaluation" looks like it may be what I need. I'll give it a go and report back. – TryTryAgain Jun 28 '16 at 18:31
  • 4
    The *shell* operators `&&` and `||` have equal precedence, unlike the usual Boolean operators in most languages. That is, `a || b && c` is the same as `(a || b) && c`, not `a || (b && c)`. – chepner Jun 28 '16 at 18:37

3 Answers3

2

You should be using this snippet to group && conditions together in one [[ ... ]].

if [[ -n $V1 && $V2 == *"contain some"* ]] || [[ -n $V3 && $V4 == *"output contains"* ]]
then
   echo "Hello $V1"
   echo "World full of: $V2"
fi
anubhava
  • 761,203
  • 64
  • 569
  • 643
1

Boolean conditions stop processing as soon as they have enough information to satisfy the test. So, if you have something like true && false && true, the last true will never be reached. A single false is enough to know that the whole test is going to be false, since you have to have all trues.

You might try adding (), which creates a subshell for each. This should effectively group the tests together, and allow the larger tests to be separate:

if ( [[ "$V1" ]] && [[ "$V2" == *"contain some"* ]] ) || ( [[ "$V3" ]] && [[ "$V4" == *"output contains"* ]] )
then
    echo "Hello $V1"
    echo "World full of: $V2"
fi

The important thing is to remember to keep your conditions separate.

DKing
  • 574
  • 5
  • 16
  • 3
    It's better to use `{ ... }` instead of `(...)` to avoid forking an unnecessary subshell. – chepner Jun 28 '16 at 18:36
  • That works as well, and it's nice to know about creating subshells like that. Also, thanks @chepner for adding your input, that's also valuable information! – TryTryAgain Jun 28 '16 at 18:39
0

I ultimately chose to use an 'order of evaluation' approach as firstly commented by @Lino referencing the third example in this answer groups of compound conditions in Bash test

if [[ "$V6" && "$V2" == *"contain some"* || ( "$V3" && "$V4" == *"output contains"* ) ]]
then
echo "Hello $V1"
echo "World full of: $V2"
fi

If this is a bad approach for some reason, I would appreciate any feedback.

I like how it is all contained within a single [[ ]] double bracket, and remains readable.

All answers and comments have been very helpful and enlightening! @DKing's answer suggesting to use subshells, @anubhava's answer cleanly combining the statements, and all of @chepner's comments!

Community
  • 1
  • 1
TryTryAgain
  • 7,632
  • 11
  • 46
  • 82