Beginner: script headache

Hi,

I am new to shell scripting, but doing my best to learn things. Today doing courses there was exercise like this:

#!/usr/bin/bash
a=$1
b=$2
if [[ $a == "A" && $b == "B" ]]
then
echo "Conditions are met"
else
echo "Conditions are not met"
fi

I improved it a little, beacuse of curiosity :smiley: to this:

#!/bin/sh

a=$1
b=$2


if [[ $a == "A" || $a == "a" && $b == "B" || $b == "b" ]]
then
        echo "Conditions are met"
else
        echo "Conditions are not met"
fi

And here is funny thing: when you put vaules to script as: ./script b b it shows that conditions are met, but they shouldn't - at least accroding to the first script they shouldn't, the thing I changed is sensitivity for small letters.

Maybe it's written wrong? Have no idea, please advise :wink:

if you execute with debugging mode, you can see the last condition was met (b=b). That's why it shows the "conditions are met"

bash-3.2$ bash -x script.sh  b b
+ a=b
+ b=b
+ [[ b == \A ]]
+ [[ b == \a ]]
+ [[ b == \b ]]
+ echo 'Conditions are met'
Conditions are met

You are having a problem with the precedence of operators. In that expression, the AND operator has higher precedence than the OR operator, so the expression in your if statement is evaluated as though you had written:

if [[ $a == "A" || ( $a == "a" && $b == "B" ) || $b == "b" ]]

To get what I think you were trying to do, you want something more like:

#!/bin/sh
a=$1
b=$2

if [[ ( $a == "A" || $a == "a" ) && ( $b == "B" || $b == "b" ) ]]
then
        echo "Conditions are met"
else
        echo "Conditions are not met"
fi

Thx, now everyting is clear about this case :slight_smile:

As an addendum you are using #!/bin/sh
This is a POSIX compatible version of it...

#!/usr/local/bin/dash
# * !/bin/sh *
a=$1
b=$2

if [ "$a" = "A" ] || [ "$a" = "a" ] && [ "$b" = "B" ] || [ "$b" = "b" ]
then
        echo "Conditions are met."
else
        echo "Conditions are not met."
fi

No. That has the same precedence problem as the code in post #1 in this thread. To do this using only POSIX-defined utilities, one way to do it would be:

#!/bin/ksh
a=$1
b=$2

if { [ "$a" = "A" ] || [ "$a" = "a" ]; } && { [ "$b" = "B" ] || [ "$b" = "b" ]; }
then
        echo "Conditions are met."
else
        echo "Conditions are not met."
fi

This was written and tested using a Korn shell, but will also work with any other POSIX-conforming shell.

1 Like

Hi Don...

I stand corrected...

Thanks...

EDIT:
Sorry about the edit but......
Just noticed the semi-colons, can you explain why they are needed?

---------- Post updated at 11:43 AM ---------- Previous update was at 10:09 AM ----------

It seems like this does work too...

#!/usr/local/bin/dash
a=$1
b=$2

if ( [ "$a" = "A" ] || [ "$a" = "a" ] ) && ( [ "$b" = "B" ] || [ "$b" = "b" ] )
then
        echo "Conditions are met."
else
        echo "Conditions are not met."
fi

Results, OSX 10.7.5, default [bash] terminal but calling 'dash'.

AMIGA:barrywalker~> cd Desktop/Code/Shell
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh a
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh b
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh "" b
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh b ""
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh a ""
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh "" a
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh a a
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh a b
Conditions are met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh b a
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh b b
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh A b
Conditions are met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh A B
Conditions are met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh a B
Conditions are met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh B B
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh B A
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh B b
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh B a
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh A A
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> ./AND_OR.sh b B
Conditions are not met.
AMIGA:barrywalker~/Desktop/Code/Shell> _

Hi wisecracker,
The command:

if ( [ "$a" = "A" ] || [ "$a" = "a" ] ) && ( [ "$b" = "B" ] || [ "$b" = "b" ] )

executes [ "$a" = "A" ] || [ "$a" = "a" ] in a subshell environment and if it evaluates to true it then evaluates [ "$b" = "B" ] || [ "$b" = "b" ] in a subshell execution environment. In this construct, the parentheses are shell operators.

The command:

if { [ "$a" = "A" ] || [ "$a" = "a" ]; } && { [ "$b" = "B" ] || [ "$b" = "b" ]; }

executes [ "$a" = "A" ] || [ "$a" = "a" ] in the current shell executionl environment and if it evaluates to true it then evaluates [ "$b" = "B" ] || [ "$b" = "b" ] in the current shelll execution environment. In this construct, the braces are shell keywords; not shell operators. The <semicolon>s are shell operators used to delimit the keywords. You could also see this written as:

if { [ "$a" = "A" ] || [ "$a" = "a" ]
   } && { [ "$b" = "B" ] || [ "$b" = "b" ]
   }

using <newline> instead of <semicolon> to delimit the brace keywords.

I used braces instead of parentheses to avoid creating two subshells.

2 Likes

Hi Don...

Thanks a lot...

The first part I had worked out by myself.

Your two derivatives I would never have known without your current post.

I checked my AudioScope.sh script as I have used '&&' inside it but not with any '||'s...

My mentor, you are... (YODA style. <wink>)