Using Case Statements in Bash
Instead of a bunch of nested if else statements, using case statements can drastically reduce the number of lines in your bash script, making it easier to understand and follow the logic.
When writing Bash scripts, it is common to check a string against a variable, and then do something based on what was specified.
Consider the following script. It checks the first argument that a user specifies, and does different things depending on what was given:
#!/usr/bin/env bash
if [[ "${1}" == "" ]]; then
echo "No shape was specified."
elif [[ "${1}" == "square" ]]; then
echo "You specified 'square'."
elif [[ "${1}" == "circle" ]]; then
echo "You specified 'circle'."
elif [[ "${1}" == "triangle" ]]; then
"You specified 'triangle'."
else
echo "Unknown shape '${1}'."
exit 1
fi
This demonstrates, albeit with no functionality, a simple task that can repeat a known shape back to a user, or if the shape is unknown, show an error message.
Everything's fine and dandy in this, and it all works as expected. But look at how many lines that took. See how you have to handle every single option that a user passes through an elif
statement?
Now imaging this program has even more options, say 20. With the current approach, you are going to be writing an if
statement with 20 elif
sections inside it.
Yeah, you'll probably be able to read it for the most part. But it's clunky, takes up too much space, and most importantly, there is a better alternative.
Introducing the case statement in Bash
This is where case statements come in. These can reduce the number of lines of code you need for checking things by a long shot compared to if
statements.
The same if
statement you used to check for the provided options could be rewritten like so inside of a case one:
#!/usr/bin/env bash
case "${1}" in
"") echo "No option was specified."; exit 1 ;;
square) echo "You specified 'square'." ;;
circle) echo "You specified 'circle'." ;;
triangle) echo "You specified 'triangle'." ;;
*) echo "Unknown shape '${1}'."; exit 1 ;;
esac
Look how much simpler and organized that looks. That is 12 lines of code in the if
statement, and only 7 in the case
one. And the more options you allow, the more drastic that difference becomes.
And that's where case
statements shine. While both are completely legible, especially at a small amount, case
statements simply make everything cleaner and more maintainable in the long run.
How do you create a case statement in Bash?
Case statements are pretty simple to understand with just a little bit of practice. With that, here's the basics to what's going on:
The first line you saw was case ${1} in
. The ${1}
gives the string that will be checked against, here being the first argument passed to the script.
For example, consider this case statement:
case linux in
windows) echo "windows" ;;
macos) echo "macos" ;;
linux) echo "linux" ;;
esac
In this example, it will always run echo "linux"
. The case *string* in
part simply specifies what string you'll be checking against, and then the options below it will run the commands whenever a match is found.
Next, you start specifying your options. Remember, you had ""
, square
, circle
, triangle
, and *
as options in your first script.
Case statements are checked up to down, and only run on the first match. Consider the following example:
case linux in
linux) echo "linux" ;;
windows) echo "windows" ;;
linux) echo "linux 2.0" ;;
esac
Here, the only command that would run would be echo "linux"
. The second match would then be skipped, as there was a match that was already found.
Each line in a case statement must also end in ;;
. This signifies the end of the commands list for an option, and that no further commands should be processed.
The arguments list in a case statement (the linux)
and windows)
parts also support wildcards. Thus, entering something like linu*)
would match both linux
and linus
.
Lastly, you end the case statement with esac
. This lets it know that you have listed everything you would like it to process, like done
would do in a for
or while
loop.
esac
is case
backwards.
Case statements in practice - argument checks
A frequent use of case statements in my own programming is checking against an arguments list.
Say you allow the following options to be provided to your program:
--vertical
--add-txt
--replace
--verify
To check this in a case statement, you could use something like this:
while [[ "${1}" != "" ]]; do
case "${1}" in
--vertical) verticle="true" ;;
--add-txt) add_txt="true" ;;
--replace) replace="true" ;;
--verify) verify="true" ;;
esac
shift 1
done
This runs a while
loop while ${1}
(the first argument given to a script) is not empty. It then checks against an argument list, and assigns variables when the provided option is set.
After checking against the case statement, it then runs shift 1
, causing the value of ${2}
(the second argument provided to a script) to move to the value of ${1}
, and repeats this against the new ${1}
(which was previously ${2}
) until ${1}
isn't set to anything.
And there you go! That's all the basics to using case statements.
Wrapping Up
Case statements can help you to write code both more quickly, and more efficiently. They also help to make things more legible and maintainable in the long run.
If you are new to Bash, you may learn the basics of Bash from scratch in our Bash Beginner Series.
Got any questions, or something not working quite right? Feel free to leave a comment below, and I'll try my best to respond back!
Sole Linux user with Ubuntu running my desktops and servers. You can check out some stuff I've made on GitHub.