402

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 ().

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user389955
  • 9,605
  • 14
  • 56
  • 98
  • 9
    You are not asking about shell conditions but [test](https://de.wikipedia.org/wiki/Test_%28Unix%29) conditions. The whole expression in your example is evaluated by `test` (`[`) and not by the shell. The shell evaluates just the exit status of `[`. – ceving Jun 17 '15 at 13:06
  • See also http://stackoverflow.com/questions/16203088/multiple-conditions-if-statement-bash-script – tripleee Dec 15 '15 at 09:08
  • Related: [Compound if statements with multiple expressions in Bash](https://stackoverflow.com/q/11267569/6862601) – codeforester Aug 15 '19 at 20:10

8 Answers8

472

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 13
    It's good to know that escaped parentheses work; as an aside: in this particular case, the parentheses aren't even needed, because `-a` actually has higher precedence than `-o` (unlike `&&` and `||` in the shell - however, _inside_ `bash` `[[ ... ]]` _conditionals_, `&&` _also_ has higher precedence than `||` ). If you wanted to avoid `-a` and `-o` for maximum robustness and portability - which the [POSIX man page itself](http://man.cx/test) suggests - you could also use _subshells_ for grouping: `if ([ $g -eq 1 ] && [ "$c" = "123" ]) || ([ $g -eq 2 ] && [ "$c" = "456" ])` – mklement0 May 09 '14 at 12:11
  • Good solution, but I like the form without all the parenthesis and brackets: `if test "$g" -eq 1 -a "$c" = "123" || test "$g" -eq 2 -a "$c" = "456"; then ...` – Mogens TrasherDK Mar 17 '18 at 07:06
  • I'm a bit late to this, but it's still a top search result, so I'd just like to note that using `&&` or `||` are also preferable as it behaves more like conditionals in other languages, and lets you short circuit conditions. For example: `if [ -n "${check_inodes}" ] && [ "$(stat -f %i "/foo/bar")" = "$(stat -f %i "/foo/baz")" ]`. Doing it this way, if `check_inodes` is empty, you avoid two calls to `stat`, whereas a larger, complex test condition must process *all* arguments before executing (which can also lead to bugs if you forget about that behaviour). – Haravikk Apr 09 '20 at 10:52
  • For anyone having further trouble making this work, ensure there are spaces between your conditions and the parentheses too. – Simon Jan 12 '23 at 11:06
226

In Bash:

if [[ ( $g == 1 && $c == 123 ) || ( $g == 2 && $c == 456 ) ]]
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • 12
    Definitely the best approach in `bash`. As an aside: in this particular case the parentheses aren't even needed, because _inside_ `[[ ... ]]` _conditionals_ `&&` actually has higher precedence than `||` - unlike _outside_ such conditionals. – mklement0 May 09 '14 at 12:16
  • Is there a way we can find out which condition matched here? Was it the one in first parenthesis or the other one? – user47 Dec 11 '15 at 19:17
  • 1
    @Firelord: You'd need to separate the conditions into an `if` / `else` statement and have the code between `then` and `fi` put in a function in order to avoid repeating it. In very simple cases you could use a `case` statement with `;&` fallthrough (in Bash 4). – Dennis Williamson Dec 11 '15 at 22:15
  • 3
    Whats the purpose of the two square brackets? – Peter Chaula Apr 06 '17 at 08:10
  • 4
    @peter: Please see my answer [here](http://stackoverflow.com/a/2188369/26428), [Conditional Constructs in the Bash Manual](https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html#Conditional-Constructs) and [BashFAQ/31](http://mywiki.wooledge.org/BashFAQ/031). – Dennis Williamson Apr 06 '17 at 16:43
  • @DennisWilliamson thank You a lot, it helped me with diff-friendly formatting `if` statement conditions, moving each OR statement to separate line – Hunter_71 Jul 30 '18 at 09:26
52

Using /bin/bash the following will work:

if [ "$option" = "Y" ] || [ "$option" = "y" ]; then
    echo "Entered $option"
fi
David Ferenczy Rogožan
  • 23,966
  • 9
  • 79
  • 68
sunitha
  • 1,468
  • 14
  • 18
11

Be careful if you have spaces in your string variables and you check for existence. Be sure to quote them properly.

if [ ! "${somepath}" ] || [ ! "${otherstring}" ] || [ ! "${barstring}" ] ; then
orkoden
  • 18,946
  • 4
  • 59
  • 50
8

In Bash, you can use the following technique for string comparison

if [ $var OP "val" ]; then
    echo "statements"
fi

Example:

var="something"
if [ $var != "otherthing" ] && [ $var != "everything" ] && [ $var != "allthings" ]; then
    echo "this will be printed"
else
    echo "this will not be printed"
fi
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ashan Priyadarshana
  • 3,119
  • 3
  • 29
  • 34
6
g=3
c=133
([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg"

Output:

efg

g=1
c=123
([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg"

Output:

abc

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
0
#!/bin/bash

current_usage=$( df -h | grep 'gfsvg-gfslv' | awk {'print $5'} )
echo $current_usage
critical_usage=6%
warning_usage=3%

if [[ ${current_usage%?} -lt ${warning_usage%?} ]]; then
echo OK current usage is $current_usage
elif [[ ${current_usage%?} -ge ${warning_usage%?} ]] && [[ ${current_usage%?} -lt ${critical_usage%?} ]]; then
echo Warning $current_usage
else
echo Critical $current_usage
fi
Community
  • 1
  • 1
  • 5
    Welcome to Stack Overflow! This question is looking for an *explanation*, not simply for working code. Your answer provides no insight for the questioner, and may be deleted. Please [edit] to explain what causes the observed symptoms. – Toby Speight May 18 '17 at 11:51
-2

You can also chain more than two conditions:

if [ \( "$1" = '--usage' \) -o \( "$1" = '' \) -o \( "$1" = '--help' \) ]
then
   printf "\033[2J";printf "\033[0;0H"
   cat << EOF_PRINT_USAGE

   $0 - Purpose: upsert qto http json data to postgres db

   USAGE EXAMPLE:

   $0 -a foo -a bar



EOF_PRINT_USAGE
   exit 1
fi
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Yordan Georgiev
  • 5,114
  • 1
  • 56
  • 53