$ {! FOO} і zsh


13

${!FOO}виконує подвійну підстановку у bash, тобто означає, що вона приймає (рядкове) значення FOO і використовує його як ім'я змінної.
zshне підтримує цю функцію.

Чи є спосіб , щоб зробити цю роботу в тому ж самому bashі zsh?

Фон:

У мене є список змінних середовища, наприклад

PATH MAIL EDITOR

і потрібно спочатку надрукувати імена змінних, а потім їх значення.

Це працює, bashале не zsh:

for VAR in LIST
do
        echo $VAR
        echo ${!VAR}
done

Це має бути якось можливо "по-старому" eval, але я не можу змусити його працювати:

for VAR in LIST
do
        echo $VAR
        echo `eval \$$VAR`
done

Я ніколи не буду розуміти, чому я не можу просто робити довільні глибокі заміни на кшталт ${${VAR}}або навіть у ${${${VAR}}}разі потреби, тому пояснення цього було б непоганим.


1
ХАХ! Я думав, що zsh повинен мати більше можливостей.
Ярослав Рахматуллін

4
Не потрібно хвалитися bash, він справді має ту саму функцію (і набагато більше), використовує лише інший малюнок, а саме ${(p)FOO}.
Profpatsch

2
@ ЯрославРахматуллін, (e)прапор розширення параметра zsh був доданий у zsh-2.6-beta17 (травень 1996 р.), (P)Прапор 3.1.5-pws-7 (1999 р.). bash's ${!var}2.0 (грудень 1996).
Стефан Шазелас

Відповіді:


18

І bash, і zsh мають спосіб виконати непряме розширення, але вони використовують різний синтаксис.

Досить просто виконати непряме розширення за допомогою eval; це працює у всіх POSIX та більшості оболонок Борна. Не забудьте правильно цитувати, якщо значення містить символи, які мають особливе значення в оболонці.

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

${${VAR}}не працює, оскільки це не функція, яку реалізує будь-яка оболонка. Річ, що знаходиться в дужках, повинна відповідати синтаксичним правилам, які не включають ${VAR}. (У zsh це підтримується синтаксисом, але робить щось інше: вкладені підстановки виконують послідовні перетворення на одне і те ж значення; ${${VAR}}еквівалентно $VARтому, що воно здійснює перетворення ідентичності двічі на значення.)


19

Коментар, під оригінальним запитанням, від Profpatsch, дає приклад ${(p)FOO}для виведення вмісту непрямої змінної посилання. Прапор неправильний або друкарський помилок, він повинен бути великим P, а не малим p. Використання: ${(P)FOO}.

Наступний повинен отримати бажаний вихід:

#! /bin/zsh
LIST=(PATH MAIL EDITOR)
for VAR in ${LIST}
do
    print "${VAR}:  ${(P)VAR}"
done

Зі сторінки людини zshexpn ; розділ - Прапор розширення параметрів :

П

  This forces the value of the parameter name to be interpreted as a further
  parameter name,  whose  value  will  be used where appropriate.  Note that
  flags set with one of the typeset family of commands (in particular case
  transformations) are not applied to the value of name used in this fashion.

  If used with a nested parameter or command substitution, the result of that
  will be taken as a parameter  name  in the  same  way.   For  example,  if
  you  have  `foo=bar'  and `bar=baz', the strings ${(P)foo}, ${(P)${foo}}, and
  ${(P)$(echo bar)} will be expanded to `baz'

Я свого часу прочитав, чому ${${${VAR}}}не дає очікуваного результату, але наразі не можу його знайти. Ви можете зробити щось на зразок наступного:

first="second" ; second="third" ; third="fourth" ; fourth="fifth"
print ${(P)${(P)${(P)first}}}
fifth

3

Ви неправильно використовуєте eval. У вашому прикладі значення $ VAR, яке передувало символу "$" (тобто "$ VALUE"), виконується як команда. Це не те, чого ти хочеш. Ви хочете оцінити розширення змінної, ім'я якої взято з іншої змінної.

$ for i in `echo PATH MAIL EDITOR`; 
    do eval moo="\${$i}" 
    echo $moo 
done
/usr/local/sbin:/usr/local/bin:/usr/sbin:/u (...)
/var/mail/root
nano

0

{ba,z}sh рішення

Ось функція, яка працює в обох {ba, z} sh. Я вважаю, що це також сумісно з POSIX.

Він попереджає, коли дається:

  • Недійсне введення
  • Більше одного аргументу
  • Ім'я змінної, яке не встановлено

# Expand the variable named by $1 into its value. Works in both {ba,z}sh
# eg: a=HOME $(var_expand $a) == /home/me
var_expand() {
  if [ "$#" -ne 1 ] || [ -z "${1-}" ]; then
    printf 'var_expand: expected one non-empty argument\n' >&2;
    return 1;
  fi
  eval printf '%s' "\"\${$1?}\""
}

У POSIX немає functionні . [[...]]sh
Стефан Шазелас

Перевірка на відсутність аргументу не повинна бути [ "$#" -eq 0 ](або, мабуть, так, [ "$#" -ne 1 ]як ви очікуєте лише одного аргументу).
Стефан Шазелас

@ StéphaneChazelas Спасибі, оновлено. Чи є опорна оболонка на відповідність POSIX? Я думаю, що shell-checkтеж може допомогти.
Том Хейл,

@ StéphaneChazelas Повторний тест, я також хочу хочу провалитись, коли мені дали єдиний нульовий аргумент. Я додав вашу другу пропозицію. Ура!
Том Хейл,

Я згоден із цитуванням ${1-}даного a='"" -o -n 1' [ -z $a ]== 0. Але чи не $#завжди буде один маркер?
Том Хейл,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.