Use una referencia de variable "dentro" de otra variable

27

Estoy seguro de que es relativamente simple, simplemente no sé cómo hacerlo.

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

Quiero algo como lo echo ${$myvar}que quiero interpretar como ${$myvar}-> ${6}->value

Brandon Kreisel
fuente
44
El término técnico es indirección variable .
Thor

Respuestas:

29

Puede hacer esto con eval, incorporado en muchos shells finos, incluido ksh:

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

El truco consiste en comillas dobles la cadena a la que se alimenta para evalque $ myvar se sustituya con "6", y barra diagonal inversa el signo de dólar exterior, de modo que se evalobtenga una cadena "$ 6".

Obtuve "% user" para la salida, pero lo probé en una máquina RHEL multiprocesador.

Bruce Ediger
fuente
44
Usted es oficialmente el Gran Maestro Supremo Exaltado de la semana b / c que incluso trabaja en el ksh indescifrablemente horrible (realmente pdksh) en OpenBSD 5.4. Si desea establecer var vv en el valor de la var cuyo nombre está en la var vn , simplemente haga vv=$( eval "echo \$$vn" ). ¡Gracias una tonelada!
execNext
25

Referencia de variable indirecta

Los shells avanzados modernos tienen un método para hacer referencia al valor de una variable cuyo nombre se almacena en otra variable. Lamentablemente, el método difiere entre ksh, bash y zsh.

En mksh ≥R39b, puede hacer myvarun nameref:

typeset -n myvar=6
echo "$myvar"

Esto no funciona en ATT ksh93 porque no admite namerefs a parámetros posicionales. En el caso de que tenga una variable que contenga un nombre de variable, puede usar este método.

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

En bash ≥2.0, puedes escribir

echo "${!myvar}"

En zsh, puedes escribir

echo ${(P)myvar}

En shells más antiguos, incluidos ksh88 y pdksh, su único recurso cuando tiene una variable que contiene otro nombre de variable y desea utilizar el valor de esta variable eval, como explica Bruce Ediger . Esta solución funciona en cualquier shell Bourne / POSIX.

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

Usando una matriz

Este es el mejor método aquí: es más simple y más portátil.

Para su caso de uso, en cualquier shell con matrices (todas las variantes ksh, bash ≥2.0, zsh), puede asignar a una variable de matriz y tomar el elemento que desee. Tenga en cuenta que las matrices ksh y bash comienzan a numerarse en 0, pero zsh comienza en 1 a menos que emita setopt ksh_arrayso emulate ksh.

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

Si desea copiar los parámetros posicionales a una variable de matriz a:

set -A a -- "$@"

En ksh93, mksh ≥R39b, bash ≥2.0 y zsh, puede usar la sintaxis de asignación de matriz:

iostat=($(iostat))
echo "${iostat[5]}"
Gilles 'SO- deja de ser malvado'
fuente
Wow, su solución 'Bourne / POSIX' también funciona en ksh / pdksh de OpenBSD 5.4. Para aplicarlo al ejemplo en mi comentario a la respuesta de Bruce Ediger anterior, simplemente hazlo eval "vv=\${$vn}". Merci beaucoup, amable señor.
execNext
1

Como lo indicó Gilles (quien proporcionó la bashparte de la respuesta), además de no invalidar la de Bruce Ediger (sobre cómo hacerlo de manera portátil eval), a continuación se explica cómo hacerlo con namerefreciente mksh(y AT&T ksh93, excepto - como comentó @Gilles - namerefs no puede referirse a parámetros posicionales en AT&T ksh, solo a parámetros nombrados):

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

También se agregó el --after setpara mejorar la resistencia.

mirabilos
fuente
A partir de ksh 93u, namerefs no puede hacer referencia a parámetros posicionales ( typeset: 6: invalid variable name).
Gilles 'SO- deja de ser malvado'
0

Otro uso de matrices

No he usado ksh ni ninguna variante durante algún tiempo, por lo que no estoy seguro de si ksh (o bash) tiene una capacidad similar. Mi caparazón principal es zsh. Utilizo matrices cuando manejo la salida de comandos como iostat porque producen múltiples líneas, y no todas las líneas tienen el mismo formato / longitud.

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

Lo anterior también omite el uso de parámetros posicionales. Ahora, si desea generar, digamos, una matriz de dispositivos:

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

Encuentro trozos más pequeños mucho más fáciles de manejar. Puede que necesite o no usar una referencia de variable indirecta, dependiendo de su código. Saber cómo funciona sigue siendo bueno saberlo. Lo uso yo mismo.

Friartek
fuente