T-One
T-One

Reputation: 25

Two AND/OR in one bash if statement

I have a special condition to test in a bash script. I thought about something like:

if [[ $categorie = "DG" || $categorie = "DI" ]] && [[ $client != "host1" || $client != "host2" ]]; then
    echo "requirements are met"
else
    echo "requirements not met"
fi

In words: If categorie is DG OR DI AND client is NOT host1 OR host2 then do stuff.

Is this correct? For me it looks like it just cares about the categorie and if the categorie condition is met it's ok and prints "requirements are met" and doesn't care about the condition for the client at all.

Upvotes: 0

Views: 247

Answers (3)

Benjamin W.
Benjamin W.

Reputation: 52122

You can get around negating OR by comparing to patterns instead of doing multiple comparisons, which simplifies the test to

if [[ $categorie == D[GI] ]] && [[ $client != host[12] ]]; then
    echo "requirements are met"
else
    echo "requirements not met"
fi

Upvotes: 0

Jonathan Leffler
Jonathan Leffler

Reputation: 753665

English and logic ascribe different meanings to negated OR — it is very easy to get confused.

Think about possible values for $client; they are 'host1', 'host2', and 'other'. If the value is 'host1' the second term in the || is true; if it is 'host2', the first term in the || is true; if it is 'other' both terms in the || are true; which means the condition is always true.

Replace that || with && — if the client is not equal to host1 and is not equal to host2.

if [[ "$categorie" = "DG" || "$categorie" = "DI" ]] &&
   [[ "$client" != "host1" && "$client" != "host2" ]]; then
    echo "requirements are met"
else
    echo "requirements not met"
fi

Or use !(client == host1 || client == host2) which can be translated to:

if [[ "$categorie" = "DG" || "$categorie" = "DI" ]] &&
   [[ ! ( "$client" = "host1" || "$client" = "host2" ) ]]; then
    echo "requirements are met"
else
    echo "requirements not met"
fi

Putting it into a test script:

for categorie in DG DI XX
do
    for client in host1 host2 other
    do
        if [[ "$categorie" = "DG" || "$categorie" = "DI" ]] &&
           [[ "$client" != "host1" && "$client" != "host2" ]]; then
            echo "(categorie $categorie; client $client): requirements are met"
        else
            echo "(categorie $categorie; client $client): requirements not met"
        fi
        if [[ "$categorie" = "DG" || "$categorie" = "DI" ]] &&
           [[ ! ( "$client" = "host1" || "$client" = "host2" ) ]]; then
            echo "(categorie $categorie; client $client): requirements are met"
        else
            echo "(categorie $categorie; client $client): requirements not met"
        fi
    done
done

Output:

(categorie DG; client host1): requirements not met
(categorie DG; client host1): requirements not met
(categorie DG; client host2): requirements not met
(categorie DG; client host2): requirements not met
(categorie DG; client other): requirements are met
(categorie DG; client other): requirements are met
(categorie DI; client host1): requirements not met
(categorie DI; client host1): requirements not met
(categorie DI; client host2): requirements not met
(categorie DI; client host2): requirements not met
(categorie DI; client other): requirements are met
(categorie DI; client other): requirements are met
(categorie XX; client host1): requirements not met
(categorie XX; client host1): requirements not met
(categorie XX; client host2): requirements not met
(categorie XX; client host2): requirements not met
(categorie XX; client other): requirements not met
(categorie XX; client other): requirements not met

The pairs of lines are always the same — the expressions are equivalent. And the results are, I believe, what you want.

Upvotes: 2

Jens
Jens

Reputation: 72639

You are correct. Since any string is always unequal to at least one of two differing strings, the part after the && is always true.

Upvotes: 0

Related Questions