Используйте ссылку на переменную «внутри» другой переменной

27

Я уверен, что это относительно просто, я просто не знаю, как это сделать.

#!/usr/bin/ksh
set `iostat`
myvar=6

Я хочу что-то подобное, echo ${$myvar}которое я хочу интерпретировать как ${$myvar}-> ${6}->value

Брэндон Крейзель
источник
4
Технический термин - переменная косвенность .
Тор

Ответы:

29

Вы можете сделать это с помощью evalвстроенных во многие тонкие оболочки, включая ksh:

#!/usr/bin/ksh
set $(iostat)
myvar=6
eval "echo \${$myvar}"

Хитрость заключается в том, evalчтобы заключить в двойную кавычку строку, которую вы передаете, чтобы $ myvar заменялось на «6», и обратную косую черту внешнего знака доллара, чтобы evalполучить строку «$ 6».

Я получил «% user» для вывода, но я попробовал его на многопроцессорной машине RHEL.

Брюс Эдигер
источник
4
Вы официально являетесь Верховным Великим Магистром недели, который даже работает над невероятно ужасным ksh (действительно pdksh) в OpenBSD 5.4. Если вы хотите установить var vv в значение var, имя которого находится в var vn , просто сделайте vv=$( eval "echo \$$vn" ). Благодаря тонну!
execNext
25

Косвенная ссылка на переменную

В современных расширенных оболочках есть метод для ссылки на значение переменной, имя которой хранится в другой переменной. К сожалению, метод отличается между ksh, bash и zsh.

В mksh ≥R39b вы можете сделать myvarnameref:

typeset -n myvar=6
echo "$myvar"

Это не работает в ATT ksh93, потому что он не поддерживает nameref для позиционных параметров. Если у вас есть переменная, содержащая имя переменной, вы можете использовать этот метод.

foo=bar
typeset -n myvar=foo
echo "$myvar"  # prints bar

В bash ≥2.0 вы можете написать

echo "${!myvar}"

В зш можно написать

echo ${(P)myvar}

В более старых оболочках, включая ksh88 и pdksh, вы можете использовать только те переменные, которые содержат имя другой переменной и хотите использовать значение этой переменной eval, как объяснил Брюс Эдигер . Это решение работает в любой оболочке Bourne / POSIX.

eval "value=\${$myvar}"
echo "$value"

Использование массива

Это лучший метод здесь: он проще и более переносим.

Для вашего случая использования в любой оболочке с массивами (все варианты ksh, bash ≥2.0, zsh) вы можете назначить переменную массива и взять нужный элемент. Помните, что массивы ksh и bash начинают нумерацию с 0, а zsh начинается с 1, если вы не выполните команду setopt ksh_arraysили emulate ksh.

set -A iostat -- $(iostat)
echo "${iostat[5]}"

Если вы хотите скопировать позиционные параметры в переменную массива a:

set -A a -- "$@"

В ksh93, mksh ≥R39b, bash ≥2.0 и zsh вы можете использовать синтаксис назначения массива:

iostat=($(iostat))
echo "${iostat[5]}"
Жиль "ТАК - перестань быть злым"
источник
Вау, ваше решение Bourne / POSIX также работает в OpenBSD 5.4 ksh / pdksh. Чтобы применить его к примеру в моем комментарии к ответу Брюса Эдигера выше, просто сделайте eval "vv=\${$vn}". Merci Beaucoup, добрый сэр.
execNext
1

Как указал Жиль (предоставивший bashчасть ответа), также не лишивший законной силы Брюса Эдигера (о том, как это сделать переносимо eval), вот как это сделать namerefв последнее время mksh(и AT & T ksh93, за исключением - как прокомментировал @Gilles - namerefs не может ссылаться на позиционные параметры в AT & T ksh, только на именованные параметры):

#!/bin/mksh
set -- $(iostat)
nameref myvar=6
echo $myvar

Добавлен --после setдля улучшения сопротивляемости тоже.

mirabilos
источник
Начиная с ksh 93u, namerefs не может ссылаться на позиционные параметры ( typeset: 6: invalid variable name).
Жиль "ТАК - перестань быть злым"
0

Другое использование массивов

Некоторое время не использовал ни ksh, ни какой-либо другой вариант, поэтому я не уверен, что ksh (или bash) имеет аналогичные возможности. Моя основная оболочка - это Zsh. Я использую массивы при обработке вывода таких команд, как iostat, потому что они выдают несколько строк, и не все строки имеют одинаковый формат / длину.

#! /bin/zsh
IOStatOutput=("${(@f)$(iostat)}") # Produces one element per line

Вышесказанное также обходит использование позиционных параметров. Теперь, если вы хотите сгенерировать, скажем, массив устройств:

for Element in {7..${#IOStatOutput}} # Devices listed in elements 7 thru the last
do
    DevList+=( ${${=IOStatOutput[Element]}[1]} )
done

Я нахожу меньшие куски намного легче в обращении. Вы можете или не можете использовать косвенную ссылку на переменную, в зависимости от вашего кода. Знать, как это работает, все еще полезно знать. Я использую это сам.

Friartek
источник