Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top
#!/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.

share|improve this question
up vote 6 down vote accepted

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
share|improve this answer
    
I will try, thank you! – Ragnar Lodbrog Mar 6 at 8:35
    
I'm curious, is there any case where this could fail where the -eq test would succeed? How does -eq handle something like 9.0? – Wildcard Mar 6 at 9:04
2  
@Wildcard As you note, case is just doing string compare whereas -eq is doing an arithmetic compare, so the former will fail for num=010 compared with 10, whereas the latter will compare equal. Only integers are allowed though. – meuh Mar 6 at 9:11
    
@Wildcard Get rid of the decimal part before calling case: unix.stackexchange.com/questions/89712/bash-float-to-integer – Murphy Mar 6 at 11:06
1  
@Wildcard, that depends a lot on the sh/[ implementation, see How can I check that two input values are correct in a bash script? – Stéphane Chazelas Mar 6 at 12:49

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
share|improve this answer
    
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 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 at 9:02
    
Actually POSIX is silent on the issue. The four arguments issue is only mentioned in non-normative text. – fpmurphy1 Apr 21 at 7:21

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
share|improve this answer
    
I need to grok it... Thank you! – Ragnar Lodbrog Mar 6 at 16:47
    
Just interesting, nothing wrong. This is four times slower than the case option. Twice slower than the ^(...) option. – BinaryZebra Mar 6 at 17:11
    
The n=$1 shift might fail to set $n for following statements. – BinaryZebra Mar 6 at 19:05
    
To avoid the 2>/dev/null you may think of using = instead of -eq. – BinaryZebra Mar 6 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 at 19:43

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')

share|improve this answer
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 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 at 11:00
    
This is great example =~ working on linux, but not in my unix system. So sad. Thanks again. – Ragnar Lodbrog Mar 6 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 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 at 18:04

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.

share|improve this answer
    
I can't use bash, it is POSIX unix OS – Ragnar Lodbrog Mar 6 at 15:15
    
@RagnarLodbrog Changed to a POSIX solution. – BinaryZebra Mar 6 at 15:54
    
Thank you! Work like a charm! – Ragnar Lodbrog Mar 6 at 16:49
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 at 19:35
    
That's why 2>/dev/null was used – cuonglm Mar 6 at 21:45

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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