11

In many languages it is possible to assign the result of a case/switch statement to a variable, rather than repeating the variable assignment many times within the case statement. Is it possible to do something like this in the Bash shell?

color_code=$(case "$COLOR" in
  (red)    1;;
  (yellow) 2;;
  (green)  3;;
  (blue)   4;;
esac)

(Or, as an aside, in any other shells?)

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
iconoclast
  • 9,057
  • 12
  • 56
  • 95

2 Answers2

7

The variable=$(...) construct will take the standard output of whatever command is in $(...) and assign it to variable. Thus, to get variable assigned the way that you want, the values have to be sent to standard output. This is easily done with the echo command:

color_code=$(case "$COLOR" in
  red)    echo 1;;
  yellow) echo 2;;
  green)  echo 3;;
  blue)   echo 4;;
esac)

This will work on bash as well as all other POSIX shells.

The Optional Left Parens

According to the POSIX standard, the left parens in a case statement is optional and the following works as well:

color_code=$(case "$COLOR" in
  (red)    echo 1;;
  (yellow) echo 2;;
  (green)  echo 3;;
  (blue)   echo 4;;
esac)

As Gilles points out in the comments, not all shells accept both forms in combination with $(...): for an impressively detailed table of compatibility, see "$( )" command substitution vs. embedded ")".

John1024
  • 73,527
  • 11
  • 167
  • 163
  • One page I checked (I don't recall where it was) listed the opening `(` as optional. I thought it might help avoid letting the `)` be misinterpreted as the closing `)` for the `$(...)` expression. – iconoclast Jul 31 '14 at 15:20
  • @iconoclast Yes. The opening `(` are optional: the code works the same without or without them. I left them off only because, for better or worse, that is tradition. The key part of the proposed solution is the use of `echo`. – John1024 Jul 31 '14 at 18:19
  • 1
    @iconoclast Older (pre-POSIX) shells didn't allow an opening `(` for `case` patterns, but some shells did and required the opening `(` when `case` is used in a command substitution. Modern shells are fine either way. See http://www.in-ulm.de/~mascheck/various/cmd-subst/ – Gilles 'SO- stop being evil' Jul 31 '14 at 22:34
  • 1
    @Gilles Thanks for that info. The depth of your knowledge is, as always, impressive. – John1024 Jul 31 '14 at 22:47
3

color_code=$(…) assigns the output of the command to the variable color_code, with final newlines stripped off. So you need to produce some output. The code you wrote attempts to execute 1 as a command.

You can use this idiom. Note that color_code will be empty if $COLOR is none of the supported values.

color_code=$(case "$COLOR" in
  (red)    echo 1;;
  (yellow) echo 2;;
  (green)  echo 3;;
  (blue)   echo 4;;
esac)

But it isn't very idiomatic. The shell language is geared towards simple combinations of simple commands. This big command substitution is awkward. The command substitution creates a subshell, which is slower than the straightforward method:

case "$COLOR" in
  red)    color_code=1;;
  yellow) color_code=2;;
  green)  color_code=3;;
  blue)   color_code=4;;
esac

The main semantic difference between the two approaches is that $(…) creates a subshell, so that any assignment, exit, redirection, etc. that is performed inside has no effect outside.

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175