11

If want to express the following test in shell (sh) :

if ( a == 1 && ( b == 1 || b == 2 )) { ... }

So far, the best I have been able to write is this :

if [[ $a -eq 1 ]]; then
  if [[ $b -eq 1 || $b -eq 2 ]]; then
     ...
  fi
fi

I don't know how to compound && and || with correct precedence. Googling has not given me any answer (tutorials only give basic examples, if any)

What is the syntax to combine those two if into one ?

  • 1
    Any reason to not use ksh? Then your initial code would require only one more pair of parenthesis: if (( a == 1 && ( b == 1 || b == 2 ))); then :; fi – manatwork Jan 10 '12 at 17:18
15

Note that [[ ]] is not in either Bourne or POSIX sh. For true sh syntax, there are several ways to do this.

Using only one [ ] pair

if [ 1 -eq "$a" -a \( 1 -eq "$b" -o 2 -eq "$b" \) ]; then
    # ...
fi

or Avoiding the POSIX -a and -o options1

if [ 1 -eq "$a" ] && { [ 1 -eq "$b" ] || [ 2 -eq "$b" ]; }; then
    # ...
fi

1 One reason for avoiding -a and -o is maximum portability - not all test or [ implementations can handle more than 4 arguments, which is precisely what you get if you chain expressions with -a and -o and \( \).

  • My scripts have the #! /bin/sh shebang, and the default shell is ksh. Why would sh not be used ? – Offirmo Jan 10 '12 at 16:45
  • 2
    @Offirmo If you declare your script as /bin/sh, it is incorrect to write the script using features not in sh. You should be aware that ksh is NOT sh, but rather a superset of sh. If you take your ksh-but-declared-as-sh script to a system where sh is not a symlink to ksh, it may break. You can avoid that problem by making sure your script matches the declaration. – jw013 Jan 10 '12 at 17:03
  • I thought that setting /bin/sh as shebang would make my scripts invoked via /bin/sh. Do you mean that my default shell (ksh) ignore the shebang and interpret it directly ? – Offirmo Jan 10 '12 at 18:04
  • @Offirmo You are correct: a shebang of /bin/sh will invoke /bin/sh. The problem is /bin/sh does not accept [[. You can either not use [[ or use a shell that does accept [[, e.g. ksh, as your shebang. – jw013 Jan 10 '12 at 22:25
  • 2
    The problem is not that much that -a/-o are not supported by some implementations but that it's not reliable for arbitrary arguments. Like [ "$a" = "$b" -o "$b" = "$c" ] would fail for values of $a like ! with many [ implementations. – Stéphane Chazelas Mar 16 '15 at 16:17
2

I tried some syntax and found that

if [[ $a -eq 1 ]] && [[ $b -eq 1 || $b -eq 2 ]]; then
 ...
fi

is working.

  • 1
    this works for bash version above 4. – Nikhil Mulley Jan 10 '12 at 17:18
2

Another POSIX approach:

if [ "$((a == 1 && ( b == 1 || b == 2 )))" -ne 0 ]; then
  ...
fi
0

The right way to do it with a mere shell:

if test 1 -eq "$a" && test 1 -eq "$b" -o 2 -eq "$b"; then 
    ...
fi

Explanation:

test here serves as parenthesis and -o as logical or. If you a logical and inside the "parenthesis" you would use -a.

More on that can be found here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.