Amit Kumar
Amit Kumar

Reputation: 313

Shell variable value in pre-defined range

I want a variable whose value should be in already defined range.

for e.g.

variable DAY=$1

should only get values Mon,Tue,Wed,Thus,Fri,Sat,Sun.

if user enter something else, it will not accept it.

I know i can do this by defining array or normally a variable storing all the days and then check variable DAY in a "for or while" loop.

But i want the simplest way without loop.

Thanks in advance.

Upvotes: 1

Views: 95

Answers (4)

Mark Reed
Mark Reed

Reputation: 95307

To expand slightly on anubhava's answer:

arr=(Mon Tue Wed Thus Fri Sat Sun)
read -p 'Enter day value: ' day
case "${arr[@]}" in *"$day"*) echo "in range";; esac

Basically, this is building an array, turning it into a string, and doing a pattern match on it.

In the context of a case expression, there's no difference between "${arr[@]}" and "${arr[*]}"; I personally would stick with *, which makes it clearer that you're building one big string instead of a list of separate ones.

With this specific set of values, you don't have to worry about substring overlap, but the above check is still overly forgiving. For instance, if you enter u, it will be considered "in range". That may not be what you want.

A more exact test would be something like this:

case "${arr[*]}" in
   "$day "*|*" $day "*|*" $day") echo "in range";;
    *) echo "not in range";;
esac

This still permits the user to enter multiple days as "Mon Tue" or similar; an easy way to fix that in this particular case is to change the read line from read day to read day _.

The "$day "* pattern will match at the beginning of the array (so if they enter Mon in this case). The *" $day" pattern will match at the end of the array (Fri), and the *" $day "* pattern will match the rest. Nothing else but an exact day string will match.

Also, here you can see how to handle the else case - a pattern of * matches anything that hasn't already matched something else, so that's the equivalent of else in a case.

Upvotes: 0

chepner
chepner

Reputation: 531605

Use the extended glob support to match exactly one of a list of choices.

range="Mon|Tue|Wed|Thu|Fri"
read day
if [[ $day = @($range) ]]; then
    echo "$day is valid"
else
    echo "$day is not valid"
fi

Upvotes: 2

anubhava
anubhava

Reputation: 785376

Without using a loop you can do something like this:

EOL=$'\n'
arr=(Mon Tue Wed Thus Fri Sat Sun)
read -p 'Enter day value: ' day
Tue

[[ $(printf "$EOL%s$EOL" "${arr[@]}") == *"$EOL$day$EOL"* ]] &&
   echo "in range" || echo "not in range"
in range

read -p 'Enter day value: ' day
ue We
[[ $(printf "$EOL%s$EOL" "${arr[@]}") == *"$EOL$day$EOL"* ]] &&
   echo "in range" || echo "not in range"
not in range

Upvotes: 1

tripleee
tripleee

Reputation: 189577

There's really no need for an array.

read -r day
case ' Mon Tue Wed Thu Fri Sat Sun ' in
  *" $day "*)
    echo "in range";;
  *) echo "not in range";;
esac

Notice the spaces around the string expressions.

If you need to be strict, you should reject "Tue Wed" which this doesn't do. It would require a second case wrapper, which is clunky but not completely unwieldy.

Upvotes: 0

Related Questions