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.