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

Вопрос или проблема

Я думаю, что пример лучше всего объяснит, что мне нужно

_v1="windows"

_v2_windows="/mnt/d"
_v2_osx="/Volumes/d"

echo $_v2_`echo $_v1`

Я хочу вывести значение _v2_windows, но используя _v1, чтобы определить, какой из двух v2 получить.

Я знаю, что могу использовать оператор case для решения проблемы, но стараюсь этого избежать.

С zsh:

  • ${(P)varname} развертывается в значение переменной, имя которой хранится в $varname. Поэтому, если $varname содержит var, ${(P)varname} развернется в то же самое, что и $var.
  • ${(e)var} развертывается в содержимое $var, но также выполняет развертывание параметров, подстановку команд и арифметическое развертывание внутри. Поэтому, если $var содержит $othervar, ${(e)var} развернется в то же самое, что и $othervar.
  • Вы можете вкладывать операторы развертывания переменных, поэтому такие конструкции, как ${(P)${var:-something}}, работают.
  • ${:-content} — это один из способов осуществить развертывание параметра в произвольный текст (здесь content).

(см. руководство для подробностей)

Таким образом, вы могли бы сделать:

_v1=windows
_v2_windows=/mnt/d
printf '%s\n' ${(P)${:-_v2_$_v1}}

Или:

printf '%s\n' ${(e)${:-\$_v2_$_v1}}

Или сделать это в два шага:

varname=_v2_$_v1
printf '%s\n' ${(P)varname}

Или:

expansions_to_evaluate=\$_v2_$_v1

printf '%s\n' ${(e)expansions_to_evaluate}

Или вы могли бы использовать стандартный синтаксис POSIX:

eval 'printf "%s\n" "${_v2_'"$_v1"'}"'

Будьте осторожны, если значение $_v1 не под вашим контролем, все это представляет собой уязвимость для инъекции произвольных команд, вам нужно будет сначала очистить значение.

Также обратите внимание, что zsh поддерживает ассоциативные массивы (и поддерживает их намного раньше bash), так что вы можете сделать:

typeset -A mnt
mnt=(
  windows /mnt/d
  osx     /Volumes/d
)
os=windows
printf '%s\n' $mnt[$os]

Что будет намного более читабельным и не будет иметь никаких проблем с безопасностью.

printf '%s\n' "${(P)$(echo "_v2_$_v1")}"

или же

var=_v2_$_v1
printf '%s\n' "${(P)var}"

В обоих случаях флаг развертывания параметров (P) используется для развертывания имени внутри ${...} в имя настоящей переменной, значение которой мы хотим получить.

Это похоже на косвенную ссылку на переменные с помощью ${!...} в оболочке bash.

Ответ или решение

Для получения значения переменной на основе другой переменной в Zsh, можно использовать несколько методов. Приведем несколько примеров.

Пример с использованием параметрического расширения (P)

Переменные можно получить с помощью параметрического расширения, где переменная, имя которой хранится в другой переменной, будет разыменована. Рассмотрим ваш пример:

_v1="windows"           # Задаем первую переменную
_v2_windows="/mnt/d"    # Задаем вторую переменную для windows
_v2_osx="/Volumes/d"    # Задаем вторую переменную для osx

# Используем параметрическое расширение
printf '%s\n' ${(P)_v2_$_v1}

В данном случае $_v1 содержит строку "windows", и выражение ${(P)_v2_$_v1} станет равным ${(P)_v2_windows}, что в конце концов будет разыменовано в значение "/mnt/d".

Альтернатива: использование переменной для хранения имени

Также можно сделать это в два шага:

varname="_v2_$_v1"      # Сохраняем имя переменной в другой переменной
printf '%s\n' ${(P)varname}  # Переразворачиваем и печатаем значение

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

Можно также использовать команду eval, чтобы получить значение переменной:

eval 'printf "%s\n" "${_v2_'"$_v1"'}"'

Однако использование eval может привести к уязвимостям, если значения переменных не контролируются.

Безопасный метод с ассоциативными массивами

Zsh поддерживает ассоциативные массивы, что гораздо безопаснее и читабельнее:

typeset -A mnt         # Создаем ассоциативный массив
mnt=(
  windows /mnt/d
  osx     /Volumes/d
)

os=windows            # Определяем какую OS используем
printf '%s\n' $mnt[$os]

Здесь массив mnt содержит пары значений, а доступ к значениям осуществляется по ключу, что делает код более устойчивым и менее подверженным ошибкам.

Заключение

Использование параметрического расширения (P) и ассоциативных массивов является наиболее предпочтительным способом получить значение переменной на основе другой переменной в Zsh. Это обеспечивает безопасность и читаемость кода.

Оцените материал
Добавить комментарий

Капча загружается...