4
#!/bin/sh
if [ $num -eq 9 -o $num -eq 75 -o $num -eq 200 ]; then
    echo "do this"
elif [ $num -eq 40 -o $num -eq 53 -o $num -eq 63]; then
    echo "do something for this"
else
    echo "for other do this"
fi

Is any other way to the shrink the expression in the if statement?  Perhaps something like

[ $num -eq (9,75,200) ]

BTW, I don't have GNU utils on this OS.

5 Answers5

7

Sometimes a different construction may end up more readable:

case $num in
9|75|200) echo "do this" ;;
40|53|63) echo "do something for this" ;;
*)        echo "for other do this" ;;
esac
meuh
  • 51,383
3

It sounds like a job for a function:

test_num() {
  n=$1; shift
  for arg do
    [ "$arg" -eq "$n" ] && return 0
  done
} 2>/dev/null

if test_num "$num" 9 75 200; then
  echo "do this"
elif test_num "$num" 40 53 63; then
  echo "do something for this"
else
  echo "for other do this"
fi
cuonglm
  • 153,898
  • Just interesting, nothing wrong. This is four times slower than the case option. Twice slower than the ^(...) option. –  Mar 06 '16 at 17:11
  • The n=$1 shift might fail to set $n for following statements. –  Mar 06 '16 at 19:05
  • To avoid the 2>/dev/null you may think of using = instead of -eq. –  Mar 06 '16 at 19:06
  • @daniel Azuelos: you don't need. If nothing equal to $n, status of last test return, which is fail – cuonglm Mar 06 '16 at 19:43
  • @cuonglm This fail: [ "" -eq "" ]; echo $? –  Mar 07 '16 at 02:44
  • @cuonglm As will any of the next a values in [ "$a" -eq "$num" ]: 0+1, 1e2, 1.0, 1.3, 1,0, -+1, etc. –  Mar 07 '16 at 02:46
3

careful, posix doesn't define test with more than 4 arguments, so your test construct is undefined. see the 6th bash pitfall

So you would need, if using test, to be more verbose:

if [ "$arg" = 9 ] || [ "$arg" = 75 ] || [ "$arg" = 200 ]

or use case instead

case "$arg" in
     9|75|200)  do something ; ;
     40|53|63)  do that ;;
      *)  else ... ;;
 esac
  • I tested with /bin/sh of ksh88, 6 argument working fine: if [ $num -eq 9 -o $num -eq 75 -o $num -eq 200 -o $num -eq 13 -o $num -eq 58 -o $num -eq 14]; then echo "do this"; fi – Ragnar Lodbrog Mar 07 '16 at 03:19
  • @ragnarLodbrog: Posix should be followed. I didn't mean it will fail everywhere and anytime. But you can't rely on this, and could have problems if you do... Maybe some other cases would fail (and some other environment could fail on that one). – Olivier Dulac Mar 07 '16 at 09:02
  • 1
    Actually POSIX is silent on the issue. The four arguments issue is only mentioned in non-normative text. – fpmurphy Apr 21 '16 at 07:21
1

If you're using bash or ksh or zsh (and maybe some others, i can't recall right now) you could use [[ ... ]] rather then [ ... ], which allows you to do regular expression matches in sh.

e.g.

if [[ "$num" =~ ^(9|75|200)$ ]] ; then
    echo "do this"
elif [[ "$num" =~ ^(40|53|63)$ ]] ; then
    echo "do something for this"
else
    echo "for other do this"
fi

NOTE: because you want an exact match on the specific numbers, it's important that the regex is anchored at both ends with ^and $, otherwise they'll match other numbers that contain them (e.g. '99' or '7500' or '163')

cas
  • 78,579
  • 1
    Well, this is non-standard and thus non-portable and it appears to be more complex than the case based solution. – schily Mar 06 '16 at 09:39
  • 2
    sorry to trigger your rabidly self-righteous anti-GNUism but this =~ operator came from ksh and was adopted by bash, so you've wasted an anti-GNU rant opportunity on a false alarm. BTW, whether an answer is 'complex' or not is irrelevant...and I did point out that it only works in certain shells. – cas Mar 06 '16 at 11:00
  • This is great example =~ working on linux, but not in my unix system. So sad. Thanks again. – Ragnar Lodbrog Mar 06 '16 at 12:14
  • 1
    Note that while [[...]] comes from ksh, the =~ operator within it was first introduced by bash AFAIK (though zsh had a -pcre-match [[...]] operator before that). – Stéphane Chazelas Mar 06 '16 at 12:48
  • @Stéphane Chazelas thank you for pointing to ksh and BTW: the bash manual from September 2006 does not mention =~ but ksh93 documents that the feature was added in May 2006. So this feature seems to be from ksh93 as well. @cas: personal attacks do not work well, if done by uninformed people ;-) [[ is from ksh88 and thus is older than bash. – schily Mar 06 '16 at 13:18
  • 1
    @schily, =~ was added in bash 3.0 in 2004. zsh's pcremodule was introduced in 2001 though the-pcre-match` operator was only added in 2004 – Stéphane Chazelas Mar 06 '16 at 18:04
0

An alternative POSIX solution:

if     printf '%s' "$num" | grep -xE '(9|75|200)' >/dev/null; then
       echo "do this"
elif   printf '%s' "$num" | grep -xE '(40|53|63)' >/dev/null; then
       echo "do something for this"
else
       echo "for other do this" 
fi

This is awfully slow ~50 times slower than the case option.


This is a shorter and I believe a simpler script, only twice the time of the case option:

#!/bin/sh

num="$1"    a='9 75 200'    b='40 53 63'

tnum() {
    for    arg
    do     [ "$arg" = "$num" ] && return 0
    done   return 1
}

if     tnum $a; then
            echo "do this"
elif   tnum $b; then
            echo "do something for this"
else
            echo "for other do this"
fi

CAVEAT: No test [ "$arg" = "$num" ] will work in all cases, this fails on 00 = 0 for example.
And a numerical test [ "$arg" -eq "$num" ] will fail to match empty values [ "" -eq "" ].

You may choose what works better in your case.