Using case for a range of numbers in Bash
Asked Answered
S

6

37

I am trying to do the following using case in Bash (in Linux).

If X is between 460 and 660, output X information.

If X is between 661 and 800, do something else.

Etc.

Right now this is what I have:

case $MovieRes in
    [461-660]*) echo "$MovieName,480p" >> moviefinal ;;
    [661-890]*) echo "$MovieName,720p" >> moviefinal ;;
    [891-1200]*) echo "$MovieName,1080p" >> moviefinal ;;
    *) echo "$MovieName,DVD" >> moviefinal ;;
esac

But somehow many of the ones that are 480p, 720p or 1080p are ending with DVD instead. The variable $MovieRes is a simple list that shows, for each line, a number between 1 and 1200. Depending on the value, case decides which "case" to apply.

I would like to know how to actually use case to accomplish this since it is a bit confusing when dealing with ranges like this.

Stacystadholder answered 27/9, 2012 at 4:16 Comment(1)
"But somehow many of the ones" ... I'm not quite sure which ones are being referred to here. Can you clarify?Clique
Y
46

In bash, you can use the arithmetic expression: ((...))

if ((461<=X && X<=660))
then
    echo "480p"
elif ((661<=X && X<=890))
then
    echo "720p"
elif ((891<=X && X<=1200))
then
    echo "1080p"
else
    echo "DVD"
fi >> moviefinal
Yore answered 27/9, 2012 at 4:48 Comment(0)
P
38

The bash case statement doesn't understand number ranges. It understands shell patterns.

The following should work:

case $MovieRes in
    46[1-9]|4[7-9][0-9]|5[0-9][0-9]|6[0-5][0-9]|660) echo "$MovieName,480p" >> moviefinal ;;
    66[1-9]|6[7-9][0-9]|7[0-9][0-9]|8[0-8][0-9]|890) echo "$MovieName,720p" >> moviefinal ;;
    89[1-9]|9[0-9][0-9]|1[0-1][0-9][0-9]|1200)       echo "$MovieName,1080p" >> moviefinal ;;
    *)                                               echo "$MovieName,DVD" >> moviefinal ;;
esac

However, I'd recommend you use an if-else statement and compare number ranges as in the other answer. A case is not the right tool to solve this problem. This answer is for explanatory purposes only.

Primacy answered 27/9, 2012 at 8:2 Comment(2)
Thank you dogbane. Now here is the problem, you answer correctly answered what I was looking for, but as you also mentioned, this would not be the correct way of doing it. The correct/easier way would be to use Kev's answer. Should I: 1. Accept your answer as the correct answer and give Kev a +1 or 2. Should I accept Kev answer as the correct one and give you a +1. I ask since you both somehow answer correctly.Stacystadholder
IMO kev's answer should be accepted because that is what you (and anyone else coming to this site) should be using. But I wouldn't mind a +1! Thanks :)Primacy
A
36

Just for the pleasure of subverting case to do as you want, you can use $((...))

case 1 in
    $(($MovieRes<= 460)))echo "$MovieName,???";;
    $(($MovieRes<= 660)))echo "$MovieName,480p";;
    $(($MovieRes<= 890)))echo "$MovieName,720p";;
    $(($MovieRes<=1200)))echo "$MovieName,1080p";;
                       *)echo "$MovieName,DVD";;
esac >> moviefinal
Angers answered 12/7, 2013 at 12:25 Comment(3)
Huh, it looks like case 1 in essentially equals case true in? That is, a case is met if the formula returns true? This is a cool use of case!Thermoelectric
Nice answer! Actually we don't need $ in parentheses: $((MovieRes<=1200)) will workLorca
$/${} is unnecessary on variables inside arithmetic expressions. Source: shellcheck.net/wiki/SC2004Xenomorphic
O
5

I was looking for the simplest solution and found it difficult to use a case statement with a number range .

Finally i found a really simple solution for zsh :

  #!/usr/bin/zsh 

 case "${var}" in

 <0-5461>)

 printf "${var} is between 0 and 5461"

 ;;

 <5462-10922>)

printf "${var} is between 5462-10922"

 ;;

 esac

and for Bash :

  #!/bin/bash

 case $((   

(var >= 0 && var <= 5461)      * 1 +   

(var > 5462 && var  <= 10922)   * 2)) in   

(1) printf "${var} is between 0 and 5461";; 

(2) printf "${var} is between 5461 and 10922";; 

esac

Hope it helps someone .

Older answered 26/10, 2019 at 22:21 Comment(1)
Your zsh solution was exactly what I was looking for! Thank you so much!Andreasandree
M
3

Similar issue which might be useful to someone ... Random additional thing I just tried out where it checks also that it is an integer, for me I wanted it to have a preset value, let the user change it, if they input the wrong data it sets to default.

    func_set_num_files(){
        echo "How many files do you want to create? (input a number 1-10000)"
        read X
        # 1, is it a number, #2 is it within max range?
        if [[ $X != *[!0-9]* ]]; then

            if ((1<=X && X<=10000)) ;then
                        echo "NUM_FILES=$X"
                        NUM_FILES=$X
                else
                        echo "Invalid input, setting to default value [ $NUM_FILES ].";sleep 3
            fi

        else
            echo "Invalid input, non-numeric values entered, setting to default value [ $NUM_FILES ].";sleep 3
        fi

    }

Another example using 'case' to check that a variable is in a range of integers :

check that $MAX is a number and that it's between 50-100 :

            case $MAX in
                ''|*[!0-9]*)
                    echo "The value $MAX is not a number !"
                    exit 1
                ;;
                *)
                    if [ $MAX -lt 50 ] || [ $MAX -gt 100 ] ;then
                        echo "The value $MAX is not between 50-100"
                        exit 1
                    fi
                    echo "Looks like we are good !"
                ;;
            esac
Messenia answered 24/1, 2014 at 14:54 Comment(1)
Thanks for coming up with a way to state numerical range(s) in a case statement, rather than just suggesting that it needs to be an if. :-)Buttermilk
S
1

This is another solution (may be useful for anybody else):

function caseInRange {
  if [ $1 -ge $2 ]; then
    if [ $1 -lt $3 ]; then echo "$1"; else echo "too high"; fi
  else
    echo "to low"
  fi
}
case $MovieRes in
  1) echo "$MovieName" >> moviefinal ;;
  "$(caseInRange $MovieRes 460 660)") echo "$MovieName,480p" >> moviefinal ;;
  "$(caseInRange $MovieRes 660 890)") echo "$MovieName,720p" >> moviefinal ;;
  "$(caseInRange $MovieRes 890 1200)") echo "$MovieName,1080p" >> moviefinal ;;
  *) echo "$MovieName,DVD" >> moviefinal ;;
esac

This way you can mix ranges and fixed values.

Staminody answered 23/3, 2023 at 18:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.