Можно ли распечатать содержимое содержимого переменной с помощью сценария оболочки? (косвенная ссылка)

13

Предположим, я объявил следующие переменные:

$ var='$test'
$ test="my string"

Если я распечатаю их содержимое, я вижу следующее:

$ echo $var
$test

$ echo $test
my string

Я хотел бы найти способ напечатать содержание содержимого $var(которое является содержанием $test). Поэтому я попытался сделать следующее:

$ echo $(echo $var)
$test

Но здесь результат есть, $testа не "my string"... Можно ли распечатать содержимое содержимого переменных с помощью bash?

Рафаэль Муйнарск
источник
Не могли бы вы отредактировать свой пост, указав, какую оболочку вы используете или какие у вас требования к переносимости?
Майкл Гомер
@MichaelHomer Конечно, но как именно я могу проверить эту информацию?
Рафаэль Муйнарск
Это больше, что вы выбираете, чем что-то, чтобы проверить. Например, запускаете ли вы свои сценарии с помощью ash, или bash, или csh, или dash, ..., или zsh, или POSIX sh, или вам нужно работать в разных системах?
Майкл Гомер
@MichaelHomer Ах, я вижу ... Я использую Bash. Я просто изменю последний вопрос и вставлю новый тег.
Рафаэль Муйнарск

Ответы:

21

Вы можете сделать это, используя косвенное расширение переменной bash (если вы можете не указывать в $своей ссылочной переменной):

$ var=test
$ test="my string"
$ echo "$var"
test
$ echo "${!var}"
my string

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

jesse_b
источник
10

Для случая, когда имя переменной, содержащейся в varпрефиксе $, можно использовать eval:

$ var='$test'
$ test="my string"
$ eval echo $var
my string

Что здесь происходит:

  • bash расширяется $varдо значения $test, производя eval echo $testкоманду;
  • evalоценивает echo $testвыражение и дает желаемый результат.

Обратите внимание, что использование evalв целом может быть опасным (в зависимости от того, что хранится в var), поэтому предпочитайте избегать его. Функция косвенного расширения лучше для вашего случая (но вам нужно избавиться от $входа $test).

Данила Кивер
источник
Конечно, это опечатка. Оригинальный пост имеет одинарные кавычки. Исправлена.
Данила Кивер
4
Я всегда рекомендую использовать двойные кавычки для ссылок на переменные (чтобы избежать проблем, связанных с неожиданным разделением слов и расширением по шаблону) С eval, вам нужно два слоя двойных кавычках, выравнивают это: eval echo "\"$var\"". Имейте в виду, это не имеет ничего общего с другими опасностями использования, evalкоторые вы упомянули.
Гордон Дэвиссон
9

Аналогичен ответу Jesse_b , но вместо ссылки на переменную используется переменная ссылки на имя (требуется bash4.3+):

$ declare -n var=test
$ test="my string"
$ echo "$var"
my string

Ссылочная переменная name varсодержит имя переменной, на которую она ссылается. Когда переменная разыменовывается как $var, возвращается значение другой переменной.

bash разрешает ссылки на имена рекурсивно:

$ declare -n var1=var2
$ declare -n var2=test
$ test="hello world"
$ echo "$var1"
hello world

Для полноты, использование ассоциативного массива (в bash4.0+) также является одним из способов решения этой проблемы, в зависимости от требований:

$ declare -A strings
$ strings[test]="my string"
$ var=test
$ echo "${strings[$var]}"
my string

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

Кусалананда
источник
3

С zsh:

$ var='$test'
$ test='*'
$ printf '%s\n' ${(e)var}
*

С любой борновидной оболочкой

$ eval 'printf "%s\n" "'"$var"'"'
*

Помните, что в этих оболочках расширения переменных должны заключаться в кавычки, чтобы не допустить split + glob ( zshбудучи исключением), и echoне могут использоваться для произвольных данных.

Поскольку и то, (e)и другое и evalесть оценка шелл-кода, важно, чтобы содержание $varоставалось в вашем контроле, иначе это может быть произвольной уязвимостью, связанной с введением команд (то же самое с ${!var}подходами).

Стефан Шазелас
источник