Search code examples
bashvariablescaseindirectionpacman-package-manager

How to interpret user input as a variable name?


This one is kind of hard to explain. Consider variables all, first, last, and some:

a="apple mcintosh"
b="banana plantain"
c="coconut cashew"
all="$a $b $c"
first="$a"
last=$c"
some="$a $c"

Here is what I have:

echo "What do you want to install?"
echo "all, first, last, or some?"
read userinput

Assuming that the user types all, his input shall be treated as the name of a variable: I want the next command to be pacman -S $all (equivalent to pacman -S apple mcintosh banana plantain coconut cashew). Likewise, if the user types both first and last, the next command has to be pacman -S $first $last (which in fact should execute pacman -S apple mcintosh coconut cashew).

I used case/esac to translate userinput into a variable, but I am looking for a more flexible and elegant solution, as this approach doesn't allow more than one input.

case $userinput in                                             
  all)   result="$all";;
  first) result="$first";;
  *)     exit;;
esac

pacman -S $result

Solution

  • What you're after is an indirect variable reference, which has the form ${!var}:

    3.5.3 Shell Parameter Expansion

    [...] If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion.

    For example:

    $ a="apple mcintosh"
    $ b="banana plantain"
    $ c="coconut cashew"
    $ all="$a $b $c"
    $ first="$a"
    $ last="$c"
    $ some="$a $c"
    $ read userinput
    all
    $ result=${!userinput}
    $ echo $result
    apple mcintosh banana plantain coconut cashew
    

    To expand multiple items, use read -a to read words into an array:

    $ read -a userinput
    first last
    $ result=$(for x in ${userinput[@]}; do echo ${!x}; done)
    $ echo $result
    apple mcintosh coconut cashew