Как интерпретировать пользовательский ввод как имя переменной?

Это довольно сложно объяснить. Рассмотрим переменные all, first, last, а также some:

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

Вот что у меня есть:

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

Предполагая, что пользователь вводит allего ввод должен рассматриваться как имя переменной: я хочу, чтобы следующая команда была pacman -S $all (эквивалентно pacman -S apple mcintosh banana plantain coconut cashew). Аналогично, если пользователь вводит оба first а также lastследующая команда должна быть pacman -S $first $last (который на самом деле должен выполнить pacman -S apple mcintosh coconut cashew).

я использовал case/esac переводить userinput в переменную, но я ищу более гибкое и элегантное решение, так как этот подход не допускает более одного ввода.

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

pacman -S $result

2 ответа

Решение

Вам нужна косвенная ссылка на переменную, которая имеет вид ${!var}:

3.5.3 Расширение параметров оболочки

[...] Если первый символ параметра является восклицательным знаком (!), Вводится уровень косвенной косвенности. Bash использует значение переменной, сформированной из остальной части параметра, в качестве имени переменной; эта переменная затем раскрывается, и это значение используется в остальной части замещения, а не в значении самого параметра. Это известно как косвенное расширение.

Например:

$ 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

Чтобы развернуть несколько элементов, используйте read -a читать слова в массив:

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

Для чтения пользовательского ввода из списка вариантов, bash select это то, что вам нужно. Кроме того, когда вы начинаете спрашивать "как динамически создать имя переменной", подумайте об ассоциативных массивах:

a="apple mcintosh"
b="banana plantain"
c="coconut cashew"

declare -A choices
choices[all]="$a $b $c"
choices[first]="$a"
choices[last]="$c"
choices[some]="$a $c"

PS3="What do you want to install? "

select choice in "${!choices[@]}"; do
    if [[ -n $choice ]]; then
        break
    fi
done

echo "you chose: ${choices[$choice]}"

Выше не обрабатывает несколько вариантов. В этом случае, затем (все еще используя массив "choices" сверху):

options=$(IFS=,; echo "${!choices[*]}")
read -rp "What do you want to install ($options)? " -a response
values=""
for word in "${response[@]}"; do
    if [[ -n ${choices[$word]} ]]; then
        values+="${choices[$word]} "
    fi
done
echo "you chose: $values"

Это использует read"s -a возможность прочитать ответ в массив. На что это похоже:

$ bash select.sh 
What do you want to install (some,last,first,all)? first middle last
you chose: apple mcintosh coconut cashew 
Другие вопросы по тегам