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

147

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

var1="this is the real value"
a="var1"
Do something to get value of var1 just using variable a.

Контекст:

У меня есть несколько AMI ( Amazon Machine Image ), и я хочу запустить несколько экземпляров каждого AMI. Как только они закончат загрузку, я хочу настроить каждый экземпляр в соответствии с его типом AMI. Я не хочу запекать множество скриптов или секретных ключей внутри какого-либо AMI, поэтому я подготовил общий сценарий запуска и поместил его на S3 с общедоступной ссылкой. В rc.local я помещаю небольшой фрагмент кода, который выбирает сценарий запуска и выполняет его. Это все, что у меня есть в AMI. Затем каждый AMI обращается к общему сценарию конфигурации, который применим ко всем AMI, и специальным сценариям настройки для каждого. Эти сценарии являются частными и для доступа к ним требуется подписанный URL-адрес.

Итак, теперь, когда я запускаю экземпляр AMI (my_private_ami_1), я передаю подписанный URL для еще одного файла, представленного на S3, который содержит подписанный URL для всех частных скриптов с точки зрения пары ключ / значение.

config_url="http://s3.amazo.../config?signature"
my_private_ami_1="http://s3.amazo.../ami_1?signature"
...
Когда запускается сценарий, он загружает указанный выше файл и sourceготово. Затем он проверяет свой тип AMI и выбирает для себя правильный сценарий установки.

ami\_type=GET AMI TYPE #ex: sets ami\_type to my\_private\_ami\_1
setup\_url=GET THE SETUP FILE URL BASED ON AMI\_TYPE # this is where this problem arises

Итак, теперь у меня есть общий код, который может запускать экземпляры независимо от их типов AMI, а экземпляры могут сами о себе позаботиться.

бхупс
источник

Ответы:

271

Вы можете использовать ${!a}:

var1="this is the real value"
a="var1"
echo "${!a}" # outputs 'this is the real value'

Это пример косвенного расширения параметра :

Основная форма расширения параметров - ${parameter}. Значение parameterподставляется.

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

Фил Росс
источник
4
Это работает для меня в OSX bash, но не в debian? На debian я получаю Bad substitutionошибку.
23inhouse
12
@ 23inhouse Вы используете свой скрипт /bin/sh? Если да, попробуйте использовать /bin/bashвместо этого. Начиная с Debian Squeeze, он /bin/shстал символической ссылкой dashвместоbash . dashне поддерживает этот конкретный синтаксис и выдаст Bad substitutionошибку.
Фил Росс
8
Есть ли способ сделать это для имени переменной, принадлежащей массиву?
Loupax
Работает на ubuntu с bash
Сергей П. aka azure
1
@DoronShai Это пример косвенного расширения параметров, описанный в Справочном руководстве Bash по адресу gnu.org/software/bash/manual/html_node/…
Фил Росс,
29
X=foo
Y=X
eval "Z=\$$Y"

устанавливает Z в "foo"

Будьте осторожны при использовании, evalтак как это может привести к случайному запуску кода через значения в ${Y}. Это может причинить вред из-за внедрения кода.

Например

Y="\`touch /tmp/eval-is-evil\`"

создаст /tmp/eval-is-evil. Это тоже могло быть rm -rf /, конечно.

попробовать-поймать-наконец
источник
спас мой хвост, TY
raffian
10

Для моих коллег-пользователей zsh способ добиться того же, что и принятый ответ, - это использовать:

${(P)a}

Это называется заменой имени параметра.

Это заставляет значение имени параметра интерпретироваться как имя дополнительного параметра, значение которого будет использоваться там, где это необходимо. Обратите внимание, что флаги, установленные с помощью одной из наборных команд (в частности, преобразований), не применяются к значению имени, используемому таким образом.

Если используется с вложенным параметром или подстановкой команды, результат будет таким же образом принят как имя параметра. Например, если у вас есть 'foo = bar' и 'bar = baz', строки $ {(P) foo}, $ {(P) $ {foo}} и $ {(P) $ (echo bar) } будет расширен до 'baz'.

Аналогично, если ссылка сама является вложенной, выражение с флагом обрабатывается так, как если бы оно было непосредственно заменено именем параметра. Будет ошибкой, если эта вложенная подстановка создает массив из более чем одного слова. Например, если 'name = assoc', где параметр assoc является ассоциативным массивом, тогда '$ {$ {(P) name} [elt]}' относится к элементу ассоциативного нижнего индекса 'elt'.

smac89
источник
9

Изменил мои ключевые слова для поиска и понял :).

eval a=\$$a
Спасибо за ваше время.

бхупс
источник
Будьте осторожны при использовании eval, поскольку это может привести к случайному запуску кода через значения в ${Y}. Смотрите мое дополнение в ответе пользователя «анон».
try-catch-finally
Это единственный рабочий ответ. Иронично, что это твое собственное!
Ctrl S
1

Была такая же проблема с массивами, вот как это сделать, если вы тоже манипулируете массивами:

array_name="ARRAY_NAME"
ARRAY_NAME=("Val0" "Val1" "Val2")

ARRAY=$array_name[@]
echo "ARRAY=${ARRAY}"
ARRAY=("${!ARRAY}")
echo "ARRAY=${ARRAY[@]}"
echo "ARRAY[0]=${ARRAY[0]}"
echo "ARRAY[1]=${ARRAY[1]}"
echo "ARRAY[2]=${ARRAY[2]}"

Это выведет:

ARRAY=ARRAY_NAME[@]
ARRAY=Val0 Val1 Val2
ARRAY[0]=Val0
ARRAY[1]=Val1
ARRAY[2]=Val2
Александр Хамон
источник
0

современные оболочки уже поддерживают массивы (и даже ассоциативные массивы). Поэтому, пожалуйста, используйте их и используйте меньше eval.

var1="this is the real value"
array=("$var1")
# or array[0]="$var1"

затем, когда вы хотите его вызвать, echo $ {array [0]}

призрачная собака74
источник
будет ли это работать, если я получу строку из аргумента командной строки (например, как $1)?
jena
0

На основании ответа: https://unix.stackexchange.com/a/111627

###############################################################################
# Summary: Returns the value of a variable given it's name as a string.
# Required Positional Argument: 
#   variable_name - The name of the variable to return the value of
# Returns: The value if variable exists; otherwise, empty string ("").
###############################################################################
get_value_of()
{
    variable_name=$1
    variable_value=""
    if set | grep -q "^$variable_name="; then
        eval variable_value="\$$variable_name"
    fi
    echo "$variable_value"
}

test=123
get_value_of test
# 123
test="\$(echo \"something nasty\")"
get_value_of test
# $(echo "something nasty")
VFein
источник
0

В bash 4.3 был введен тест '-v' для набора переменных. В то же время было добавлено объявление nameref. Эти две функции вместе с оператором косвенного обращения (!) Обеспечивают упрощенную версию предыдущего примера:

get_value()
{
  declare -n var_name=$1
  if [[ -v var_name ]]
  then
    echo "${var_name}"
  else
    echo "variable with name <${!var_name}> is not set"
  fi
}

test=123
get_value test
123

test="\$(echo \"something nasty\")"
get_value test
$(echo "something nasty")

unset test
get_value test
variable with name <test> is not set

Поскольку этот подход устраняет необходимость в eval, он более безопасен. Этот код проверен в bash 5.0.3 (1).

user8150417
источник