Use uma referência variável "dentro" de outra variável

27

Tenho certeza de que é relativamente simples, simplesmente não sei como fazê-lo.

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

Eu quero algo como o echo ${$myvar}que eu quero interpretado como ${$myvar}-> ${6}->value

Brandon Kreisel
fonte
4
O termo técnico é indireto variável .
Thor

Respostas:

29

Você pode fazer isso com eval, embutido em muitos shells finos, incluindo o ksh:

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

O truque é colocar aspas duplas na string que você alimenta para evalque $ myvar seja substituído por "6" e barra invertida no cifrão externo, para evalobter uma string "$ 6".

Eu obtive "% user" para a saída, mas tentei em uma máquina RHEL com vários processadores.

Bruce Ediger
fonte
3
Você é oficialmente o Grão-Mestre Supremo e Exaltado da semana, b / c, que até trabalha com o ksh horrível (realmente pdksh) no OpenBSD 5.4. Se você deseja definir var vv para o valor da var cujo nome está na var vn , basta fazer vv=$( eval "echo \$$vn" ). Muito obrigado!
execNext
25

Referência de variável indireta

Os shells avançados modernos têm um método para referenciar o valor de uma variável cujo nome é armazenado em outra variável. Infelizmente, o método difere entre ksh, bash e zsh.

No mksh ≥R39b, você pode criar myvarum nameref:

typeset -n myvar=6
echo "$myvar"

Isso não funciona no ATT ksh93 porque não suporta namerefs para parâmetros posicionais. No caso de você ter uma variável que contém um nome de variável, você pode usar esse método.

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

No bash ≥2.0, você pode escrever

echo "${!myvar}"

No zsh, você pode escrever

echo ${(P)myvar}

Em shells mais antigos, incluindo ksh88 e pdksh, seu único recurso é quando você tem uma variável que contém outro nome de variável e deseja usar o valor dessa variável eval, conforme explicado por Bruce Ediger . Esta solução funciona em qualquer shell Bourne / POSIX.

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

Usando uma matriz

Este é o melhor método aqui: é mais simples e mais portátil.

Para o seu caso de uso, em qualquer shell com matrizes (todas as variantes do ksh, bash ≥2,0, zsh), você pode atribuir a uma variável da matriz e pegar o elemento que deseja. Cuidado para que as matrizes ksh e bash iniciem a numeração em 0, mas o zsh inicia em 1, a menos que você emita setopt ksh_arraysou emulate ksh.

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

Se você deseja copiar os parâmetros posicionais para uma variável de matriz a:

set -A a -- "$@"

No ksh93, mksh ≥R39b, bash ≥2.0 e zsh, você pode usar a sintaxe de atribuição de matriz:

iostat=($(iostat))
echo "${iostat[5]}"
Gilles 'SO- parar de ser mau'
fonte
Uau, sua solução 'Bourne / POSIX' também funciona no ksh / pdksh do OpenBSD 5.4. Para aplicá-lo ao exemplo no meu comentário à resposta de Bruce Ediger acima, basta fazê-lo eval "vv=\${$vn}". Merci beaucoup, gentil senhor.
precisa saber é o seguinte
1

Conforme indicado por Gilles (que forneceu a bashparte da resposta), também não invalidando a de Bruce Ediger (sobre como fazê-lo de forma portável), veja evalcomo fazê-lo namerefrecentemente mksh(e na AT&T ksh93, exceto - como comentou o @Gilles - namerefs não pode se referir a parâmetros posicionais no AT&T ksh, apenas a parâmetros nomeados):

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

Adicionado o --depois setpara melhorar a resistência também.

mirabilos
fonte
No ksh 93u, namerefs não pode fazer referência a parâmetros posicionais ( typeset: 6: invalid variable name).
Gilles 'SO- stop be evil'
0

Outro uso de matrizes

Não utilizo o ksh ou qualquer variante há algum tempo, por isso não tenho certeza se o ksh (ou bash) tem uma capacidade semelhante. Meu shell principal é o zsh. Eu uso matrizes ao lidar com a saída de comandos como o iostat porque eles produzem várias linhas, e nem todas as linhas têm o mesmo formato / comprimento.

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

O acima também ignora o uso de parâmetros posicionais. Agora, se você deseja gerar, digamos, uma matriz de dispositivos:

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

Acho pedaços menores muito mais fáceis de manusear. Você pode ou não precisar usar referência variável indireta, dependendo do seu código. Saber como funciona ainda é uma boa coisa para saber. Eu mesmo uso.

Friartek
fonte