Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to represent multiple conditions in a shell if statement?

Tags:

bash

shell

unix

I want to represent multiple conditions like this:

if [ ( $g -eq 1 -a "$c" = "123" ) -o ( $g -eq 2 -a "$c" = "456" ) ]    then       echo abc;   else       echo efg;    fi   

but when I execute the script, it shows

syntax error at line 15: `[' unexpected, 

where line 15 is the one showing if ....

What is wrong with this condition? I guess something is wrong with the ().

like image 249
user389955 Avatar asked Sep 29 '10 22:09

user389955


People also ask

How do you write an if statement with multiple conditions?

When you combine each one of them with an IF statement, they read like this: AND – =IF(AND(Something is True, Something else is True), Value if True, Value if False) OR – =IF(OR(Something is True, Something else is True), Value if True, Value if False) NOT – =IF(NOT(Something is True), Value if True, Value if False)

What is && and || in shell script?

The operators "&&" and "||" shall have equal precedence and shall be evaluated with left associativity. For example, both of the following commands write solely bar to standard output: $ false && echo foo || echo bar $ true || echo foo && echo bar.


1 Answers

Classic technique (escape metacharacters):

if [ \( "$g" -eq 1 -a "$c" = "123" \) -o \( "$g" -eq 2 -a "$c" = "456" \) ] then echo abc else echo efg fi 

I've enclosed the references to $g in double quotes; that's good practice, in general. Strictly, the parentheses aren't needed because the precedence of -a and -o makes it correct even without them.

Note that the -a and -o operators are part of the POSIX specification for test, aka [, mainly for backwards compatibility (since they were a part of test in 7th Edition UNIX, for example), but they are explicitly marked as 'obsolescent' by POSIX. Bash (see conditional expressions) seems to preempt the classic and POSIX meanings for -a and -o with its own alternative operators that take arguments.


With some care, you can use the more modern [[ operator, but be aware that the versions in Bash and Korn Shell (for example) need not be identical.

for g in 1 2 3 do     for c in 123 456 789     do         if [[ ( "$g" -eq 1 && "$c" = "123" ) || ( "$g" -eq 2 && "$c" = "456" ) ]]         then echo "g = $g; c = $c; true"         else echo "g = $g; c = $c; false"         fi     done done 

Example run, using Bash 3.2.57 on Mac OS X:

g = 1; c = 123; true g = 1; c = 456; false g = 1; c = 789; false g = 2; c = 123; false g = 2; c = 456; true g = 2; c = 789; false g = 3; c = 123; false g = 3; c = 456; false g = 3; c = 789; false 

You don't need to quote the variables in [[ as you do with [ because it is not a separate command in the same way that [ is.


Isn't it a classic question?

I would have thought so. However, there is another alternative, namely:

if [ "$g" -eq 1 -a "$c" = "123" ] || [ "$g" -eq 2 -a "$c" = "456" ] then echo abc else echo efg fi 

Indeed, if you read the 'portable shell' guidelines for the autoconf tool or related packages, this notation — using '||' and '&&' — is what they recommend. I suppose you could even go so far as:

if [ "$g" -eq 1 ] && [ "$c" = "123" ] then echo abc elif [ "$g" -eq 2 ] && [ "$c" = "456" ] then echo abc else echo efg fi 

Where the actions are as trivial as echoing, this isn't bad. When the action block to be repeated is multiple lines, the repetition is too painful and one of the earlier versions is preferable — or you need to wrap the actions into a function that is invoked in the different then blocks.

like image 98
Jonathan Leffler Avatar answered Sep 20 '22 02:09

Jonathan Leffler