$ {! FOO} und zsh


13

${!FOO}Führt eine Doppelsubstitution in durch bash, dh, es wird der (Zeichenfolge-) Wert von FOO als Variablenname verwendet.
zshunterstützt diese Funktion nicht.

Gibt es eine Möglichkeit, diese Arbeit in bashund gleich zu machen zsh?

Hintergrund:

Ich habe eine Liste von Umgebungsvariablen, wie

PATH MAIL EDITOR

und wollen zuerst die Variablennamen und danach ihre Werte drucken.

Das funktioniert in bashaber nicht zsh:

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

Es sollte irgendwie "auf die alte Art" möglich sein eval, aber ich kann es nicht zum Laufen bringen:

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

Ich werde nie verstehen, warum ich nicht einfach willkürliche tiefe Substitutionen wie ${${VAR}}oder sogar ${${${VAR}}}wenn nötig durchführen kann, daher wäre auch eine Erklärung dafür nett.


1
HAH! Ich dachte, zsh sollte mehr Funktionen haben.
Ярослав Рахматуллин

4
Man muss nicht damit prahlen bash, es hat in der Tat die gleiche Funktion (und vieles mehr), es wird nur ein anderes Muster verwendet, nämlich ${(p)FOO}.
Profpatsch

2
@ ЯрославРахматуллин, das (e)Parametererweiterungsflag von zsh wurde in zsh-2.6-beta17 (Mai 1996) hinzugefügt, das (P)Flag in 3.1.5-pws-7 (1999). Bash ${!var}in 2.0 (Dezember 1996).
Stéphane Chazelas

Antworten:


18

Sowohl bash als auch zsh können indirekt expandieren, verwenden jedoch eine andere Syntax.

Es ist einfach genug, eine indirekte Erweiterung mit durchzuführen eval. Dies funktioniert in allen POSIX- und den meisten Bourne-Shells. Achten Sie auf korrekte Anführungszeichen, falls der Wert Zeichen enthält, die in der Shell eine besondere Bedeutung haben.

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

${${VAR}}funktioniert nicht, weil es keine Funktion ist, die von einer Shell implementiert wird. Das in geschweiften Klammern stehende Element muss Syntaxregeln entsprechen, die keine enthalten ${VAR}. (In zsh wird diese Syntax unterstützt, sie führt jedoch etwas anderes aus: Verschachtelte Ersetzungen führen aufeinanderfolgende Transformationen für denselben Wert durch; ${${VAR}}entspricht, $VARda hierdurch die Identitätsumwandlung für den Wert zweimal ausgeführt wird.)


19

Der Kommentar unter der ursprünglichen Frage von Profpatsch gibt das Beispiel ${(p)FOO}für die Ausgabe des Inhalts einer indirekten Variablenreferenz. Das Flag ist falsch oder ein Tippfehler. Es sollte ein P in Großbuchstaben und kein p in Kleinbuchstaben sein. Verwendung: ${(P)FOO}.

Folgendes sollte die gewünschte Ausgabe erzeugen:

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

Aus der Manpage zshexpn ; section - Parameter-Erweiterungsflags :

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'

Einmal las ich, warum ${${${VAR}}}nicht die erwartete Ausgabe erzeugt wird, aber zu diesem Zeitpunkt kann ich es nicht finden. Sie können Folgendes tun:

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

3

Sie verwenden eval nicht richtig. In Ihrem Beispiel würde der Wert von $ VAR, dem ein "$" (dh "$ VALUE") vorangestellt ist, als Befehl ausgeführt. Das ist nicht was du willst. Sie möchten die Erweiterung einer Variablen auswerten, deren Name einer anderen Variablen entnommen ist.

$ 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 Lösung

Hier ist eine Funktion, die sowohl in {ba, z} sh funktioniert. Ich glaube, es ist auch POSIX-konform.

Es warnt, wenn gegeben:

  • Null-Eingabe
  • Mehr als ein Argument
  • Ein Variablenname, der nicht festgelegt ist

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

Es gibt keine functionnoch [[...]]in POSIX sh.
Stéphane Chazelas

Die Prüfung auf kein Argument sollte mit erfolgen [ "$#" -eq 0 ](oder wahrscheinlich, [ "$#" -ne 1 ]da Sie nur ein Argument erwarten).
Stéphane Chazelas

@ StéphaneChazelas Danke, aktualisiert. Gibt es eine Referenzshell für die POSIX-Konformität? Ich denke, shell-checkkönnte auch helfen.
Tom Hale

@ StéphaneChazelas Was den Test betrifft, möchte ich auch scheitern, wenn ein einzelnes Nullargument angegeben wird. Ich habe Ihren 2. Vorschlag hinzugefügt. Prost!
Tom Hale

Ich bin damit einverstanden, dass ${1-}angegeben wird a='"" -o -n 1' [ -z $a ]== 0. Aber wird es nicht $#immer ein einziges Zeichen sein?
Tom Hale
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.