We previously looked at looping in bash, and creating conditional statements using if and else but there are cases where using case statements makes the logic simpler, and clearer.
This is the basic layout of a case statement:
case expression in
case1)
statement1
statement2
;;
case2)
statement1
statement2
;;
*)
statement1
;;
esac
We can use the case statement to test for simple values, such as strings or integers, in fact, when you are testing through a range of words (e.g [A - G]), you are better off using case statement. Case statement doesn't work too well with integers, but using it works superb when testing alphabetical ranges.
The case statement will first expand the expression and then it will try to match it in turn with each specified case. When a match is found, all the statements are executed until the ;;. This is the end of the code for that specific match.
If no match is found, it would match the statement indicated by *, this is similar to else statement.
Consider the following example:
#!/bin/bash
# Script to evaluate grades
# Usage: grade.sh student grade
# Author: The_Devsrealm_Guy
# Date: September, 2020
# Website: https://devsrealm.com
if [ ! $# -eq 2 ] ; then
echo "Usage is <studentname><grade>"
exit 2
fi
case $2 in
[A-C]|[a-c]) echo "$1, Excellent Result!"
;;
[Dd]) echo "$1, Good, but you can try better next time!"
;;
[E-F]|[e-f]) echo "$1, Not Impressive, but you could do a lot better next year"
;;
*) echo "No Grade for the input you provided $1"
esac
This line: if [ ! $# -eq 2 ] ; then, which is an if statement checks if two arguments have been supplied to the script, $# is an argument identifier that accepts multiple arguments, we want no more than 2 argument or less supplied to the argument identifier, if the condition fails, the script will exit with an error state:
echo "Usage is <studentname><grade>" exit 2 fi
If the condition is true, the case statement expands the expression, which is the value of the $2 variable in the above script. This would represent the grade that we supply, we then try to match first against the letters A through to C in both upper-case and lower-case.
[A-C] is used to match A or B or C. The vertical bar then adds an additional OR to compare with a, b, or c:
[A-C]|[a-c]) echo "$1, Excellent Result!" ;;
We make a similar test for other supplied grades, [Dd], and [E-F]|[e-f]), if the supplied grade isn't matched in our test, it jumps to:
*) echo "No Grade for the input you provided $1"
The esac is used to end the case statements, it is a word "case", written in backword
This is the output, when I provide a wrong argument:
user@server:~/bin$ grade.sh Devsrealm
Usage is <studentname><grade>
and here is the output, when I provided the right argument:
user@server:~/bin$ grade.sh Devsrealm A
Devsrealm, Excellent Result!
Let's take another example, the following example test against multiple age:
#!/bin/bash
# Author: The_Devsrealm_Guy
# Script to evaluate indivudal age
# Usage: entrance.sh student grade
# Website: https://devsrealm.com
# Date: September 2020
if [ ! $# -eq 2 ] ; then
echo "You must provide <name <age>"
exit 1
fi
case $2 in
[1-9]|1[0-7]) echo "$1, You are below 18, try later"
;;
1[8-9]|2[0-9]|30) echo "$1, Congratulation, you are eligble"
;;
3[1-9]|4[0-9]|50) echo "$1, You are older than the age requirement, sorry"
;;
*) echo "Not available!"
esac
Before I start the explanation of how the above code works, I'll love to point out that bash case statement doesn't understand number ranges, it works well when comparing alphabets ranges, e.g [A-Z], but the logic is not quite that black and white with integers.
There are two ways we can circumvent this, we can either use an if statement (but that would defeat the purpose of this guide since we are talking about case statement) or shell patterns, which is what I used in the above script.
Let's dive deep into what I did, it is super simple, so, I'll urge you to pay attention:
if [ ! $# -eq 2 ] ; then, which is an if statement checks if two arguments have been supplied to the script, $# is an argument identifier that accepts multiple arguments, we want no more than 2 argument or less supplied to the argument identifier, if the condition fails, the script will exit with an error state:
echo "Usage is <name> <age>" exit 2 fi
Now unto the pattern:
[1-9]|1[1-7]) echo "$1, You are below 18, try later" - I am testing if the integer that the user supplied is less than 18, it would have been nice if you can just do [1-17], but it won't work because bash case statement doesn't understand that, it understands pattern, so, if you do 1-17, it would match only digit 1, and 17, likewise [1-83] does not mean the numbers 1 to 83, but the digits from 1 to 8, and 3.
So, doing [1-9]|1[1-7]) matches 1 - 17; under the hood: "1-9" matches number 1 to 9, the vertical symbol "|" means OR 1 [1-7], this appends 1 to 1-7, e.g 11, 12, 13...17
This is the pattern I applied for the other statements too, if the supplied age isn't matched in our test, it jumps to:
*) echo "Not available!"
If I supplied a wrong argument, I get:
user@server:~/bin$ entrance.sh Devsrealm
You must provide
If I supplied the right argument, I get:
user@server:~/bin$ entrance.sh Devsrealm 18
Devsrealm, Congratulation, you are eligible
We can make the script more kinda trivial by reading user input, and performing the case operation on it:
#!/bin/bash
# Author: The_Devsrealm_Guy
# Script to evaluate individual age
# Usage: entrance.sh student grade
# Website: https://devsrealm.com
# Date: September 2020
read -p "What is your name? " name
read -p "Your age? " age
if [[ -z $age || -z $name ]]; then
echo "You must provide "
exit 1
fi
case $age in
[1-9]|1[0-7]) echo "$name, You are below 18, try later"
;;
1[8-9]|2[0-9]|30) echo "$name, Congratulation, you are eligble"
;;
3[1-9]|4[0-9]|50) echo "$name, You are older than the age requirement, sorry"
;;
*) echo "Not available!"
esac
exit 0
The only difference here is that we are using a read command with the -p option, and then include the string that appear in the prompt. The last argument (name or age) is the variable we want to populate, so whatever input the user type is stored in this variable.
if [[ -z $age || -z $name ]] is used to check if the variable isset, we are basically checking if the value we expected has been supplied, if it is, then we continue the case operation, if not, it spits out an instructional usage.
Here is the output:
$: What is your name? The Devsrealm Guy
$: Your age? 25
The Devsrealm Guy, Congratulation, you are eligble
EOF