$ {! FOO} i zsh

13

${!FOO}wykonuje podwójne podstawienie w bash, co oznacza, że ​​pobiera wartość (ciąg) FOO i używa jej jako nazwy zmiennej.
zshnie obsługuje tej funkcji.

Czy istnieje sposób, aby to działało tak samo w bashi zsh?

Tło:

Mam listę zmiennych środowiskowych, takich jak

PATH MAIL EDITOR

i chcą najpierw wydrukować nazwy zmiennych, a następnie ich wartości.

Działa to, bashale nie zsh:

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

Powinno to być jakoś możliwe „po staremu” eval, ale nie mogę zmusić go do działania:

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

Nigdy nie zrozumiem, dlaczego nie mogę po prostu dokonywać dowolnych głębokich zmian, takich jak, ${${VAR}}a nawet, ${${${VAR}}}jeśli zajdzie taka potrzeba, więc wyjaśnienie tego również byłoby miłe.

Profpatsch
źródło
1
HAH! Myślałem, że zsh powinien mieć więcej funkcji.
Ярослав Рахматуллин
4
Nie trzeba się chwalić bash, to rzeczywiście ma tę samą funkcję (i wiele więcej), używa tylko innego wzorca, a mianowicie ${(p)FOO}.
Profpatsch,
2
@ ЯрославРахматуллин, (e)flaga rozszerzenia parametru zsh została dodana w zsh-2.6-beta17 (maj 1996), (P)flaga w 3.1.5-pws-7 (1999). bash's ${!var}w wersji 2.0 (grudzień 1996).
Stéphane Chazelas

Odpowiedzi:

18

Zarówno bash, jak i zsh mają sposób na wykonanie pośredniej rozbudowy, ale używają innej składni.

Łatwo jest przeprowadzić ekspansję pośrednią za pomocą eval; działa to we wszystkich POSIX-ach i większości powłok Bourne'a. Staraj się cytować poprawnie, jeśli wartość zawiera znaki, które mają specjalne znaczenie w powłoce.

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

${${VAR}}nie działa, ponieważ nie jest to funkcja implementowana przez żadną powłokę. Rzeczy w nawiasach klamrowych muszą być zgodne z regułami składni, które nie obejmują ${VAR}. (W zsh jest to obsługiwana składnia, ale robi coś innego: zagnieżdżone podstawienia wykonują kolejne transformacje na tej samej wartości; ${${VAR}}jest to równoważne, $VARponieważ wykonuje to transformację tożsamości dwukrotnie na wartości).

Gilles „SO- przestań być zły”
źródło
19

Komentarz pod pierwotnym pytaniem autorstwa Profpatscha podaje przykład ${(p)FOO}do wyprowadzenia treści odwołania do zmiennej pośredniej. Flaga jest niepoprawna lub literówka, powinna być wielką literą P, a nie małą literą p. Zastosowanie: ${(P)FOO}.

Poniższe powinny dać pożądany wynik:

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

Ze strony podręcznika zshexpn ; sekcja - Flagi rozszerzeń parametrów :

P.

  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'

Kiedyś przeczytałem, dlaczego ${${${VAR}}}nie daje oczekiwanych wyników, ale w tej chwili nie mogę ich znaleźć. Możesz zrobić coś takiego:

first="second" ; second="third" ; third="fourth" ; fourth="fifth"
print ${(P)${(P)${(P)first}}}
fifth
Friartek
źródło
3

Nie używasz eval poprawnie. W twoim przykładzie wartość $ VAR poprzedzona znakiem „$” (tj. „$ VALUE”) zostałaby wykonana jako polecenie. Nie tego chcesz. Chcesz ocenić rozszerzenie zmiennej, której nazwa pochodzi od innej zmiennej.

$ 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
Ярослав Рахматуллин
źródło
0

{ba,z}sh rozwiązanie

Oto funkcja, która działa zarówno w {ba, z} sh. Wierzę, że jest również zgodny z POSIX.

Ostrzega, gdy podano:

  • Brak danych wejściowych
  • Więcej niż jeden argument
  • Nazwa zmiennej, która nie jest ustawiona

# 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?}\""
}
Tom Hale
źródło
Nie ma functionani [[...]]w POSIX sh.
Stéphane Chazelas
Sprawdzanie braku argumentu nie powinno być [ "$#" -eq 0 ](lub prawdopodobnie, [ "$#" -ne 1 ]ponieważ oczekujesz tylko jednego argumentu).
Stéphane Chazelas
@ StéphaneChazelas Dzięki, zaktualizowano. Czy istnieje powłoka odniesienia dla zgodności z POSIX? Myślę, że też shell-checkmoże pomóc.
Tom Hale,
@ StéphaneChazelas Jeśli chodzi o test, chcę też zawieść, gdy otrzymam pojedynczy argument zerowy. Dodałem twoją drugą sugestię. Twoje zdrowie!
Tom Hale,
Zgadzam się z podaniem ${1-}podano a='"" -o -n 1' [ -z $a ]== 0. Ale czy nie $#zawsze będzie pojedynczy token?
Tom Hale,