case
-statements
Moving on to the next line:
case "$arg" in
...
esac
If you know another programming language, you're familiar with the concept of case statements- they're a form of conditional branching, not entirely different from the if-blocks that we saw earlier. Let's familiarize ourselves with the way Bash in particular handles them.
Bash docs on case
I try help case
in my Bash terminal, and get the following:
bash-3.2$ help case
case: case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac
Selectively execute COMMANDS based upon WORD matching PATTERN. The
`|' is used to separate multiple patterns.
bash-3.2$
It's pretty short, and doesn't tell me much more than I already know.
Example of Bash's case
However, this link has a good explanation of Bash's case statement syntax. It's much too long to copy/paste in its entirety, but there's a lot of good stuff in it.
Here is a specific example of a case
statement that the above article provides, which we can use to deconstruct how they work in Bash:
#!/bin/bash
echo -n "Enter the name of a country: "
read COUNTRY
echo -n "The official language of $COUNTRY is "
case $COUNTRY in
Lithuania)
echo -n "Lithuanian"
;;
Romania | Moldova)
echo -n "Romanian"
;;
Italy | "San Marino" | Switzerland | "Vatican City")
echo -n "Italian"
;;
*)
echo -n "unknown"
;;
esac
We open, of course, with the case
keyword, followed by the case expression and the in
keyword. The statement ends with the esac
keyword.
Each case is defined by one or more patterns included on the same line, separated by the |
operator. The )
operator terminates a pattern list. A pattern can include special characters to take advantage of pattern matching.
A pattern and its associated commands are known as a clause. Each clause must be terminated with ;;
. It is a common practice to use the wildcard asterisk symbol *
as a final pattern to define the default case. This pattern will always match.
The commands corresponding to the first pattern that matches the expression are executed. If no pattern is matched, the return status is zero. Otherwise, the return status is the exit status of the executed commands (aka the clause).
For the most part, compared to (for example) a Javascript or Ruby case statement, the only thing that's likely to be new here is the syntax. There are a few differences, such as when you want to purposely fall through from one case statement to the next, but we won't need to know about that for the purposes of grokking RBENV.
Experiment- building a simple case
statement
To solidify our understanding of how Bash handles case statements, let's build a simple one here. I start by updating my "foo" script to look like the following:
#!/usr/bin/env bash
echo "$@"
I then run it as follows, to make sure it works:
$ ./foo 1
1
$
Next, I wrap the existing code inside a "case" statement with only the default case implemented:
#!/usr/bin/env bash
case "$@" in
*)
echo "$@"
;;
esac
I run it again with the same arguments, to make sure nothing has changed:
$ ./foo 1
1
$
Next, I add a few non-default conditions:
#!/usr/bin/env bash
case "$@" in
"1")
echo "One"
;;
"2")
echo "Two"
;;
"3")
echo "Three"
;;
*)
echo "$@"
;;
esac
When I test the different edge cases, I get:
$ ./foo 1
One
$ ./foo 2
Two
$ ./foo 3
Three
$ ./foo 4
4
$
Lastly, I try adding a clause with more than one pattern:
#!/usr/bin/env bash
case "$@" in
"1")
echo "One"
;;
"2")
echo "Two"
;;
"3")
echo "Three"
;;
"4" | "5")
echo "Either four or five"
;;
*)
echo "$@"
;;
esac
When I run it, I get:
$ ./foo 4
Either four or five
$ ./foo 5
Either four or five
$ ./foo 6
6
No surprises so far- all the examples worked the way we'd expect.
Moving On
Let's move on to the first case block in RBENV's shim file.