Verwenden Sie eine Variablenreferenz "innerhalb" einer anderen Variablen

27

Ich bin sicher, es ist relativ einfach, ich weiß nur nicht, wie ich es machen soll.

#!/usr/bin/ksh
set `iostat`
myvar=6

Ich möchte so etwas wie das, echo ${$myvar}was ich als ${$myvar}-> ${6}-> interpretieren möchtevalue

Brandon Kreisel
quelle
4
Der Fachbegriff ist variable Indirektion .
Thor

Antworten:

29

Sie können dies mit evalvielen integrierten feinen Shells tun , einschließlich ksh:

#!/usr/bin/ksh
set $(iostat)
myvar=6
eval "echo \${$myvar}"

Der Trick besteht darin, die Zeichenfolge, die Sie evaleingeben, in doppelte Anführungszeichen zu setzen, sodass $ myvar durch "6" ersetzt wird, und das äußere Dollarzeichen mit einem Backslash zu versehen, sodass die evalZeichenfolge "$ 6" angezeigt wird .

Ich habe "% user" für die Ausgabe erhalten, aber ich habe es auf einem Multiprozessor-RHEL-Computer versucht.

Bruce Ediger
quelle
3
Sie sind offiziell der Höchste Erhabene Großmeister der Woche b / c, der sogar mit dem unergründlich schrecklichen ksh (wirklich pdksh) in OpenBSD 5.4 arbeitet. Wenn Sie var vv auf den Wert der Variable setzen möchten, deren Name in der Variablen vn enthalten ist , tun Sie dies einfach vv=$( eval "echo \$$vn" ). Danke vielmals!
ExecNext
25

Indirekte Variablenreferenz

Moderne erweiterte Shells verfügen über eine Methode zum Verweisen auf den Wert einer Variablen, deren Name in einer anderen Variablen gespeichert ist. Leider unterscheidet sich die Methode zwischen ksh, bash und zsh.

In mksh ≥R39b können Sie myvareinen nameref machen:

typeset -n myvar=6
echo "$myvar"

Dies funktioniert in ATT ksh93 nicht, da es keine Namensreferenzen für Positionsparameter unterstützt. Wenn Sie eine Variable haben, die einen Variablennamen enthält, können Sie diese Methode verwenden.

foo=bar
typeset -n myvar=foo
echo "$myvar"  # prints bar

In Bash ≥2.0 können Sie schreiben

echo "${!myvar}"

In zsh können Sie schreiben

echo ${(P)myvar}

In älteren Shells, einschließlich ksh88 und pdksh, können Sie nur dann darauf zurückgreifen, wenn Sie eine Variable mit einem anderen Variablennamen haben und den Wert dieser Variablen verwenden möchten eval, wie von Bruce Ediger erläutert . Diese Lösung funktioniert in jeder Bourne / POSIX-Shell.

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

Verwenden eines Arrays

Dies ist die beste Methode hier: Es ist einfacher und portabler.

Für Ihren Anwendungsfall können Sie in jeder Shell mit Arrays (alle ksh-Varianten, bash ≥2.0, zsh) eine Array-Variable zuweisen und das gewünschte Element übernehmen. Beachten Sie, dass ksh- und bash-Arrays mit 0 nummeriert werden, zsh jedoch mit 1, sofern Sie nicht setopt ksh_arraysoder ausgeben emulate ksh.

set -A iostat -- $(iostat)
echo "${iostat[5]}"

Wenn Sie die Positionsparameter in eine Array-Variable kopieren möchten a:

set -A a -- "$@"

In ksh93, mksh ≥R39b, bash ≥2.0 und zsh können Sie die Array-Zuweisungssyntax verwenden:

iostat=($(iostat))
echo "${iostat[5]}"
Gilles 'SO - hör auf böse zu sein'
quelle
Wow, Ihre 'Bourne / POSIX'-Lösung funktioniert auch in OpenBSD 5.4s ksh / pdksh. Um es auf das Beispiel in meinem Kommentar zur obigen Antwort von Bruce Ediger anzuwenden, tun Sie einfach Folgendes eval "vv=\${$vn}". Merci Beaucoup, gütiger Herr.
ExecNext
1

Wie Gilles (der den bashTeil der Antwort lieferte) anzeigte und Bruce Edigers (wie man es portabel macht) ebenfalls nicht ungültig machte eval, ist hier beschrieben, wie man es namerefin den letzten mksh(und AT & T ksh93, außer - wie @Gilles kommentierte - namerefs macht kann nicht auf Positionsparameter in AT & T verweisen (nur auf benannte Parameter):

#!/bin/mksh
set -- $(iostat)
nameref myvar=6
echo $myvar

Das --After wurde hinzugefügt, um die setWiderstandsfähigkeit zu verbessern.

Mirabilos
quelle
Seit ksh 93u können namerefs keine Positionsparameter referenzieren ( typeset: 6: invalid variable name).
Gilles 'SO- hör auf böse zu sein'
0

Eine andere Verwendung von Arrays

Ich habe seit einiger Zeit weder ksh noch eine andere Variante verwendet, daher bin ich mir nicht sicher, ob ksh (oder bash) über eine ähnliche Funktion verfügt. Meine primäre Shell ist zsh. Ich verwende Arrays, wenn ich die Ausgabe von Befehlen wie iostat bearbeite, weil sie mehrere Zeilen erzeugen und nicht alle Zeilen das gleiche Format / die gleiche Länge haben.

#! /bin/zsh
IOStatOutput=("${(@f)$(iostat)}") # Produces one element per line

Das Obige umgeht auch die Verwendung von Positionsparametern. Wenn Sie nun beispielsweise ein Array von Geräten generieren möchten, gehen Sie wie folgt vor:

for Element in {7..${#IOStatOutput}} # Devices listed in elements 7 thru the last
do
    DevList+=( ${${=IOStatOutput[Element]}[1]} )
done

Ich finde kleinere Stücke viel einfacher zu handhaben. Abhängig von Ihrem Code müssen Sie möglicherweise eine indirekte Variablenreferenz verwenden oder nicht. Zu wissen, wie es funktioniert, ist immer noch eine gute Sache zu wissen. Ich benutze es selbst.

Friartek
quelle