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.

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
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 7 '16 at 3: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 7 '16 at 9: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 7:21
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
  • Just interesting, nothing wrong. This is four times slower than the case option. Twice slower than the ^(...) option. – user79743 Mar 6 '16 at 17:11
  • The n=$1 shift might fail to set $n for following statements. – user79743 Mar 6 '16 at 19:05
  • To avoid the 2>/dev/null you may think of using = instead of -eq. – user79743 Mar 6 '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 6 '16 at 19:43
  • @cuonglm This fail: [ "" -eq "" ]; echo $? – user79743 Mar 7 '16 at 2:44
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')

  • 1
    Well, this is non-standard and thus non-portable and it appears to be more complex than the case based solution. – schily Mar 6 '16 at 9: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 6 '16 at 11:00
  • This is great example =~ working on linux, but not in my unix system. So sad. Thanks again. – Ragnar Lodbrog Mar 6 '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 6 '16 at 12:48
  • 1
    @schily, =~ was added in bash 3.0 in 2004. zsh's ` pcre module was introduced in 2001 though the -pcre-match operator was only added in 2004 – Stéphane Chazelas Mar 6 '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.

  • I can't use bash, it is POSIX unix OS – Ragnar Lodbrog Mar 6 '16 at 15:15
  • @RagnarLodbrog Changed to a POSIX solution. – user79743 Mar 6 '16 at 15:54
  • 1
    Changing the logic to compare string instead of integer can cause false positive, e.g [ 00 = 0 ] compare to [ 00 -eq 0 ]. – cuonglm Mar 6 '16 at 19:35
  • That's why 2>/dev/null was used – cuonglm Mar 6 '16 at 21:45
  • @cuonglm What Change? My answer aims to have shorter tests: tnum $a, which it does very well. – user79743 Mar 7 '16 at 2:57

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.