Case statements evaluate to strings

mbac32768 picture mbac32768 · Feb 17, 2010 · Viewed 46.5k times · Source

I've caught the functional programming bug, so naturally nothing is good enough for me anymore. ;)

So, in bash one could write:

case $status in
  "foo") status="bar" ;;
  "baz") status="buh" ;;
   *) status=$status ;;
esac

but I'm afraid of typos, so I'd prefer to write:

status=case $status in
  "foo") "bar" ;;
  "baz") "buh" ;;
  *) $status ;;
esac

The second form is invalid since the case evaluates to the exit code of the last executed command, which is not at all what I'm looking for.

Are there easy hacks to achieving what I am looking for?

Answer

Laurence Gonsalves picture Laurence Gonsalves · Feb 17, 2010

If you're sure status will only be one line, you could do something like this with sed:

status=$(echo "$status" | sed -e 's:^foo$:bar:' -e 's:^baz$:buh:')

You may also be able to get something to work with bash's built-in substitution. This almost works (I don't know of any way to get exact matching only):

status=${status/foo/bar}
status=${status/baz/buh}

If your goal is just to be more "functional" (and not to make your code more typo-proof), you could do this:

status=$(
  case "$status" in
    ("foo") echo "bar" ;;
    ("baz") echo "buh" ;;
    (*) echo "$status" ;;
  esac)

Though honestly, bash is probably one of the worst languages to try and be functional in. It was really designed with a more imperative mindset, as illustrated by the fact that you can't easily compose expressions. See in the second code snippet how I had to break it into two separate statements? If bash were designed to be functional you'd be able to write something like this instead:

status=${{status/baz/buh}/foo/bar}

But that does not work.

I'd suggest only using bash for simpler scripts, and for more complicated stuff use something like Python or Ruby. They'll let you write more functional code without having to constantly wrestle with the language.