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

My target is to verify a range of number with (only with case + esac), and print the range. So for example:

  • If the number is between 0 and 80, print >=0<=80
  • If the number is between 81 and 100 then print >=81<=100
  • etc.

The problem with my script below print only >=0<=90 only if the number between 0 and 9. How to fix my script, so that it will print correct output according to the number range?

#!/bin/ksh
read number 
case $number in 
 [0-80])  echo ">=0<=80";; 
 [81-100]) echo ">=81<=100";; 
 [101-120]) echo ">=101<=120";;
 [121-300]) echo ">=121<=300";;
esac
share|improve this question
up vote 3 down vote accepted

case is only for pattern matching, it won't do arithmetic evaluation (except maybe if you consider zsh's <x-y> extended pattern matching operator). The [...] is only to match one character (or collating element in some implementations) based on the set specified within. So for instance [0-80] would match one character if it's one of 0 to 8 or 0 (that is, one of 0, 1, 2, 3, 4, 5, 6, 7, 8).

You could match numbers with patterns like:

case $(($number)) in
  ([0-9]|[1-7][0-9]|80) echo ">=0<=80";;
  (8[1-9]|9[0-9]|100) echo ">=81<=100";;
  ... and so on
esac

But you can easily see that it's not the right tool.

The [...] matches one character against the list of specified characters, so [121-300] matches for any character that is either 1, 2, 1 to 3, 0 or 0, so it's the same as [0-3] or [0123].

Use:

if [ "$number" -ge 0 ] && [ "$number" -le 80 ]; then
  echo ">=0<=80"
elif [ "$number" -ge 81 ] &&  [ "$number" -le 100 ]; then
  echo ">=81<=100"
elif ... and so on
  ...
fi

Another way to use case would be like:

case $((
  (number >= 0 && number <= 80)   * 1 +
  (number > 80 && number <= 100)  * 2 +
  (number > 100 && number <= 120) * 3 +
  (number > 120 && number <= 300) * 4)) in
  (1) echo ">=0<=80";;
  (2) echo ">=81<=100";;
  (3) echo ">=101<=120";;
  (4) echo ">=121<=300";;
  (0) echo "None of the above";;
esac

Or use the ternary operator (x ? y : z):

case $((
  number >= 0 && number <= 80   ? 1 :
  number > 80 && number <= 100  ? 2 :
  number > 100 && number <= 120 ? 3 :
  number > 120 && number <= 300 ? 4 : 0)) in...

Or like @mikeserv, think outside the box, reverse the case logic and match 1 against the value of those arithmetic comparisons.

share|improve this answer
    
+1, consider if [ n < 0 ] - elif [ n <= 80 ] - elif [ n <= 100 ] ... - else. Less typing, less error-prone. – peterph Feb 19 '13 at 12:29

Actually this is really easy to do. The thing about case is that it will always expand only as much as is needed to find the first match against a pattern. That's spec'd behavior. And so you can just set it up with a known string and evaluate the patterns' expansions.

case  1:${number:--} in
(1:*[!0-9]*|1:0*[89]*)
  ! echo NAN
;;
($((number<81))*)
    echo "$number >=0<=80"
;;
($((number<101))*)
    echo "$number >=81<=100"
;;
($((number<121))*)
    echo "$number >=101<=120"
;;
($((number<301))*)
    echo "$number >=121<=300"
;;
esac

case will never expand any more of those patterns than it has to in order to find a leading 1 in the pattern. This is especially important when working with user input, because it means you can safely verify the contents of $number before ever trying to put it in an arithmetic expansion context in the same case statement in which you actually do put it in a math expansion.

share|improve this answer
    
👍 I like the way you think outside/around the box. – Stéphane Chazelas Oct 29 '15 at 20:41
    
@StéphaneChazelas - i like case. theres some cool stuff you can do with $((math)) and case - especially surrounding assignments in patterns that never happen until they need to - and you can even build parse trees that expand nested recursions if you populate the patterns with an alias chain. its the fastest way ive found to get a shell to do stuff like character translation and to swap characters for bytes values. it can be pretty fast - C-Locale ASCII+ <> octal very worst case is 7 basic POSIX pattern expansions. – mikeserv Oct 29 '15 at 21:08

This is not very nice but you can use this :

 #!/bin/ksh

read number  

case $number in
[0-9]|[1-7][0-9]|80) echo  echo ">=0<=80";;
8[1-9]|9[0-9]|100) echo ">=81<=100";;
10[1-9]|11[0-9]|120) echo ">=101<=120";;
12[1-9]|130) echo ">=121<=300";;
esac
share|improve this answer
    
You may want to "canonify" the number with $(($number)) to cover for numbers like "001" or "0x99"... That would also cover for " 12" and "12+12" which may or may not be desirable. – Stéphane Chazelas Feb 19 '13 at 12:02

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.