$ {! FOO} e zsh


13

${!FOO}esegue una doppia sostituzione in bash, nel senso che prende il valore (stringa) di FOO e lo usa come nome di variabile.
zshnon supporta questa funzione.

C'è un modo per rendere questo lavoro uguale in bashe zsh?

Sfondo:

Ho un elenco di variabili d'ambiente, come

PATH MAIL EDITOR

e voglio prima stampare i nomi delle variabili e successivamente i loro valori.

Questo funziona bashma non zsh:

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

Dovrebbe essere in qualche modo possibile "alla vecchia maniera" eval, ma non riesco a farlo funzionare:

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

Non capirò mai perché non posso semplicemente fare sostituzioni profonde arbitrarie come ${${VAR}}o anche ${${${VAR}}}se necessario, quindi anche una spiegazione sarebbe buona.


1
HAH! Pensavo che zsh avrebbe dovuto avere più funzionalità.
Ярослав Рахматуллин,

4
Non c'è bisogno di vantarsi bash, ha infatti la stessa funzione (e molto di più), utilizza solo uno schema diverso, vale a dire ${(p)FOO}.
Profpatsch,

2
@ ЯрославРахматуллин, il (e)flag di espansione dei parametri di zsh è stato aggiunto in zsh-2.6-beta17 (maggio 1996), il (P)flag in 3.1.5-pws-7 (1999). bash's ${!var}in 2.0 (dicembre 1996).
Stéphane Chazelas,

Risposte:


18

Sia bash che zsh hanno un modo per eseguire l'espansione indiretta, ma usano una sintassi diversa.

È abbastanza facile eseguire l'espansione indiretta usando eval; funziona in tutte le shell POSIX e nella maggior parte dei Bourne. Fai attenzione a citare correttamente nel caso in cui il valore contenga caratteri che hanno un significato speciale nella shell.

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

${${VAR}}non funziona perché non è una funzionalità implementata da alcuna shell. La cosa all'interno delle parentesi graffe deve essere conforme alle regole di sintassi che non includono ${VAR}. (In zsh, questa è una sintassi supportata, ma fa qualcosa di diverso: le sostituzioni nidificate eseguono trasformazioni successive sullo stesso valore; ${${VAR}}è equivalente a $VARpoiché esegue la trasformazione dell'identità due volte sul valore.)


19

Il commento, sotto la domanda originale, di Profpatsch fornisce l'esempio ${(p)FOO}per produrre il contenuto di un riferimento a una variabile indiretta. Il flag non è corretto o è un refuso, dovrebbe essere una P maiuscola e non una minuscola p. Usa: ${(P)FOO}.

Quanto segue dovrebbe produrre l'output desiderato:

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

Dalla pagina man di zshexpn ; sezione - Flag di espansione dei parametri :

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'

Una volta ho letto perché ${${${VAR}}}non produce l'output che ti aspettavi, ma in questo momento non riesco a trovarlo. Puoi fare qualcosa di simile al seguente:

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

3

Non stai usando correttamente eval. Nel tuo esempio il valore di $ VAR preceduto da un "$" (ovvero "$ VALUE") verrebbe eseguito come comando. Non è quello che vuoi. Si desidera valutare l'espansione di una variabile il cui nome è preso da un'altra variabile.

$ 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 soluzione

Ecco una funzione che funziona sia in {ba, z} sh. Credo che sia anche conforme a POSIX.

Avvisa quando viene dato:

  • Input nullo
  • Più di un argomento
  • Un nome variabile che non è impostato

# 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?}\""
}

Non c'è function[[...]]in POSIX sh.
Stéphane Chazelas,

Il controllo per nessun argomento dovrebbe essere con [ "$#" -eq 0 ](o probabilmente [ "$#" -ne 1 ]come ti aspetti un solo argomento).
Stéphane Chazelas,

@ StéphaneChazelas Grazie, aggiornato. Esiste una shell di riferimento per la conformità POSIX? Sto pensando che shell-checkpotrebbe aiutare anche.
Tom Hale,

@ StéphaneChazelas Per quanto riguarda il test, voglio anche fallire quando viene fornito un singolo argomento null. Ho aggiunto il tuo secondo suggerimento. Saluti!
Tom Hale,

Sono d'accordo con la citazione ${1-}data a='"" -o -n 1' [ -z $a ]== 0. Ma non sarà $#sempre un singolo token?
Tom Hale,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.