Utilizzare un riferimento di variabile "dentro" un'altra variabile


27

Sono sicuro che è relativamente semplice, semplicemente non so come farlo.

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

Voglio qualcosa come quello echo ${$myvar}che voglio interpretato come ${$myvar}-> ${6}->value


4
Il termine tecnico è indiretta variabile .
Thor,

Risposte:


29

Puoi farlo con eval, integrato in molte belle shell, tra cui ksh:

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

Il trucco è di citare due volte la stringa con cui ti nutri in evalmodo che $ myvar venga sostituito con "6", e di rovesciare il segno del dollaro esterno, in modo da evalottenere una stringa "$ 6".

Ho ottenuto "% user" per l'output, ma l'ho provato su una macchina RHEL multi-processore.


4
Sei ufficialmente il Gran Maestro Supremo Esaltato della settimana b / c che lavora persino sull'insondabile terribile ksh (davvero pdksh) in OpenBSD 5.4. Se vuoi impostare var vv sul valore di var il cui nome è in var vn , fallo e basta vv=$( eval "echo \$$vn" ). Grazie mille!
execNext

25

Riferimento variabile indiretto

Le moderne shell avanzate hanno un metodo per fare riferimento al valore di una variabile il cui nome è memorizzato in un'altra variabile. Sfortunatamente il metodo differisce tra ksh, bash e zsh.

In mksh ≥R39b, puoi creare myvaruna nameref:

typeset -n myvar=6
echo "$myvar"

Questo non funziona in ATT ksh93 perché non supporta namerefs ai parametri posizionali. Nel caso in cui si disponga di una variabile contenente un nome variabile, è possibile utilizzare questo metodo.

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

In bash ≥2.0, è possibile scrivere

echo "${!myvar}"

In zsh, puoi scrivere

echo ${(P)myvar}

Nelle shell più vecchie, inclusi ksh88 e pdksh, l'unica soluzione è quando hai una variabile che contiene un altro nome di variabile e vuoi usare il valore di questa variabile eval, come spiegato da Bruce Ediger . Questa soluzione funziona in qualsiasi shell Bourne / POSIX.

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

Utilizzando un array

Questo è il metodo migliore qui: è più semplice e portatile.

Per il tuo caso d'uso, in qualsiasi shell con array (tutte le varianti di ksh, bash ≥2.0, zsh), puoi assegnare a una variabile array e prendere l'elemento che desideri. Attenzione che le matrici ksh e bash iniziano la numerazione da 0, ma zsh inizia da 1 a meno che non si emetta setopt ksh_arrayso emulate ksh.

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

Se si desidera copiare i parametri posizionali in una variabile di matrice a:

set -A a -- "$@"

In ksh93, mksh ≥R39b, bash ≥2,0 e zsh, è possibile utilizzare la sintassi dell'assegnazione dell'array:

iostat=($(iostat))
echo "${iostat[5]}"

Wow, la tua soluzione 'Bourne / POSIX' funziona anche in ksh / pdksh di OpenBSD 5.4. Per applicarlo all'esempio nel mio commento alla risposta di Bruce Ediger sopra, basta eval "vv=\${$vn}". Merci beaucoup, gentile signore.
execNext

1

Come indicato da Gilles (che ha fornito la bashparte della risposta), anche non invalidando Bruce Ediger (su come farlo in modo portabile con eval), ecco come farlo con namerefdi recente mksh(e AT&T ksh93, tranne - come ha commentato @Gilles - namerefs non può fare riferimento a parametri posizionali in AT&T ksh, solo a parametri nominati):

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

Aggiunto anche il --dopo setper migliorare la resistenza.


A partire da ksh 93u, namerefs non può fare riferimento ai parametri posizionali ( typeset: 6: invalid variable name).
Gilles 'SO- smetti di essere malvagio' il

0

Un altro uso di array

Non ho usato né ksh né alcuna variante per qualche tempo, quindi non sono sicuro se ksh (o bash) abbia una capacità simile. La mia shell principale è zsh. Uso le matrici quando gestisco l'output da comandi come iostat perché producono più righe e non tutte le linee hanno lo stesso formato / lunghezza.

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

Quanto sopra ignora anche l'uso di parametri posizionali. Ora, se vuoi generare, diciamo, un array di dispositivi:

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

Trovo blocchi più piccoli molto più facili da gestire. Potrebbe essere necessario o meno utilizzare il riferimento alla variabile indiretta, a seconda del codice. Sapere come funziona è ancora una buona cosa da sapere. Lo uso da solo.

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.