Case statements are a type of statement that is used in Bash. They are used to describe a situation or condition and to give information about what will happen if that situation or condition is met. To use a case statement in Bash, you need to first create a variable called $case . Then, you can use the following command to create a case statement: $case “test” in | test -n 1 | test -n 2 | echo “ok” | test -n 3 | echo “error”
In this tutorial we look at Bash case based conditional statements. We review practical examples, and discuss when a Bash case .. esac statement is best employed over a more traditional if .. then .. fi statement.
Bash case .. esac Statements in Bash
Natively available from within the Bash shell, case conditional statements, formulated using the case and esac idioms (an idiom is a word or a group of words, or a keyword, with established usage patterns; a form of expressing a computer language), one can construct a complex conditional case-by-case (hence the term case) lookup and execution-per-case statement.
The lookup part of a case based statement is sometimes called a select, leading to the term select/case statement (or the reverse), used in some other coding languages like VBA, to refer to the same type of programming paradigm. Yet another set of languages (like C#, C++, Java and JavaScript) call this a switch or switch/case statement. All these have similar meanings and operations.
Let’s build our first case .. esac statement using a simple script.
A simple case .. esac script
We define our test script test.sh as follows:
Next, we make our script executable by executing chmod +x test.sh and then we execute the script itself, each time passing a different option as input.
In our case opening clause, we specify that we want to use the first input option ($1) as the variable to be evaluated. Our case .. esac statement will verify each option (as indicated by the text preceding a ) clause closing idiom) and then process the code block (after the ) idiom) for the correct result.
Note that there are two ; end-of-statement terminators at the end of each line. The reason for this is simple; normal Bash syntax requires you to have at least one terminating idiom ; (unless you are at the end of a line already), but inside the case .. esac statement, we need an additional ; to indicate that we are terminating a code block for a specific case code execution block.
We can see how we have defined code for both the cases where 1 and 2 are passed to the script, respectively. However, what happens when we pass something else? In that case, we want to jump to our other clause, indicated by a universal * idiom.
We see how we pass 1 or 2 to the script, the corresponding correct action is taken; Option was 1/2!. We also see how if we do something else, the * code block is executed correctly, and nicely our first passed variable to the script is displayed also. What would happen if we passed no option to our script? The case .. esac code would still be evaluated, and since nothing matches except for our catch-all * clause, that one is executed:
So what would happen if we were to assume, something which may seem logical, that we can leave off a single ; statement from the end of the line of each case .. esac clause simply because at the same time an EOL (end of line) is reached?
Note that only a single terminating ; was used in this script. Bash does not allow this, and an error is produced; the final terminating syntax of ;; is required, and something to keep in mind when writing case .. esac statements
Why case .. esac And Not if?
We could also write out the previous code using an if statement:
This looks comparable in code quality and shortness in comparison with our case .. esac code, and in the given situations and specific use cases like these, often an if .. then .. elif .. else .. fi based solution will do just fine.
That said, if and when the complexity of the selection becomes more complex or larger, or if there are multi-layered case selections to be performed (note that case .. esac can be nested), it may pay off to use case .. esac statements instead.
Another nice feature of case .. esac statements is that one can list two possible options for a single code execution block, as we will see in our next example, however this an also be done with standard Bash if statements, using for example the -a (and) and -o (or) options from within our test clause and specifying a secondary test clause. For example, if [ “${1}” -eq 1 -o “${1}” -eq 3 ]; then … Note that here we also used numerical comparisons instead of text based comparisons as in our example above.
For more information on if .. then .. else .. elif .. fi conditionals, you can also read our article Conditional Testing in Bash: if, then, else, elif.
Expanding Our Case
Let us expand on our previous script and make our conditions a bit more complex:
In this case, we have listed two possible options for each code block and separated them by a | separator. Note how everything works well and the inputs 1 and 3 are correctly parsed. In the second code block we have created a nested case, and used each possible input to the second code block (namely 2 and a) as new options in the nested/secondary esac.
This again works flawlessly. The flow through the code is that in the first case statement, the 2|‘a’ branch is taken is 2 or a is passed, and then the second case ensures that either 2 or a is selected individually. Of interest is also the use of ;; every time we want to terminate a code block for a specific option, and this also applies to terminating the nested case .. esac, again with ;;, i.e. esac;;.
Wrapping up
In this tutorial, we looked at practical examples of Bash based case .. esac statements. We also saw where case statements are a better choice then more traditional if .. then .. fi based statements.
If Bash interests you, you may find Primer: Bash Loops: for, while, and until, Bash Process Termination Hacks and How to Correctly Parse File Names in Bash to be of interest.
Enjoy!