Retorno indireto de todos os elementos em uma matriz

7

A página man do Bash descreve o uso de ${!a} para retornar o conteúdo da variável cujo nome é o conteúdo de a (um nível de indireção).

Gostaria de saber como retornar todos os elementos em uma matriz usando isso, ou seja,

a=(one two three)
echo ${a[*]}

retorna

one two three

Eu gostaria de:

b=a
echo ${!b[*]}

para retornar o mesmo. Infelizmente, isso não acontece, mas retorna 0 .

Atualizar

Dadas as respostas, percebo agora que meu exemplo era simples demais, já que é claro, algo como:

b=("${a[@]}")

Alcançará exatamente o que eu disse que precisava.

Então, aqui está o que eu estava tentando fazer:

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST=LIST_$whichone
LIST=${!_LIST[*]}

É claro que a leitura cuidadosa da página man do Bash mostra que isso não funcionará como esperado, porque a última linha simplesmente retorna os índices do "array" $_LIST (não um array de jeito nenhum).

Em qualquer caso, o seguinte deve fazer o trabalho (como apontado):

LIST=($(eval echo \${$_LIST[*]}))

ou ... (a rota que fui, eventualmente):

LIST_lys="lys1 lys2"
...
LIST=(${!_LIST})

Supondo, claro, que os elementos não contenham espaços em branco.

    
por Eric Smith 06.09.2011 / 06:46

4 respostas

5

Eu acho que o uso de referência indireta da variável bash deve ser tratado literalmente.

Para o seu exemplo original:

a=(one two three)
echo ${a[*]} # one two three
b=a
echo ${!b[*]} # this would not work, because this notation 
              # gives the indices of the variable b which
              # is a string in this case and could be thought
              # as a array that conatins only one element, so
              # we get 0 which means the first element
c='a[*]'
echo ${!c} # this will do exactly what you want in the first
           # place

Para o último cenário real, acredito que o código abaixo funcionaria.

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[*]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one

É melhor usar a notação "${var[@]}" , que evita bagunçar o $IFS e a expansão de parâmetro. Aqui está o código final.

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[@]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one
                     # It is essential to have ${!_LIST} quoted
    
por 02.10.2012 / 12:54
7

Você precisa copiar os elementos explicitamente. Para uma matriz indexada:

b=("${a[@]}")

Para um array associativo (note que a é o nome da variável array, não uma variável cujo valor é o nome de uma variável array):

typeset -A b
for k in "${!a[@]}"; do b[$k]=${a[$k]}; done

Se você tiver o nome da variável em uma matriz, poderá usar o método elemento a elemento com uma etapa extra para recuperar as chaves.

eval "keys=(\"\${!$name[@]}\")"
for k in "${keys[@]}"; do eval "b[\$k]=\${$name[\$k]}"; done

(Atenção, o código nesta postagem foi digitado diretamente em um navegador e não foi testado).

    
por 06.09.2011 / 09:38
2

${!b[*]} expande para os índices usados na matriz b .

O que você gostaria que fosse feito em duas etapas, então eval ajudará: eval echo \${$b[*]} . (Observe o \ , que garante que o primeiro $ passe o primeiro passo, a expansão da variável, e será expandido apenas no segundo passo em eval .)

De acordo com a Expansão do parâmetro ! é usada para expansão indireta ( {!a} ), Prefixo de correspondência de nomes ( ${!a*} ) e Lista de chaves de matriz ( ${!a[*]} ). Como a lista de chaves de matriz tem a mesma sintaxe que a expansão de expansão de matriz + expansão indireta, o último não é suportado como é.

    
por 06.09.2011 / 09:10
0

Para acessar matrizes indiretamente, basta adicionar [@] à variável indireta b=a[@] .

Se essas variáveis estiverem definidas:

a=(one two three)
printf '<%s> ' "${a[@]}"; echo

Então, isso funcionará:

b="a[@]"
printf '<%s> ' "${!b}"

Ou simplesmente:

echo "${!b}"

Esse array pode ser copiado como este:

newArr=( "${!b}" )

E depois impresso com:

declare -p newArr

Em um script:

#!/bin/bash
a=(one two three)
echo "original array"
printf '<%s> ' "${a[@]}"; echo

echo "Indirect array with variable b=a[@]"
b="a[@]"
printf '<%s> ' "${!b}"; echo

echo "New array copied in newArr, printed with declare"
newArr=( "${!b}" )
declare -p newArr

Claro, todos os itens acima irão copiar um array não esparso. Um em que todos os índices têm um valor.

matrizes esparsas

Um array esparso é aquele que pode ter elementos não definidos.
Por exemplo, a[8]=1234 define um elemento e, no bash, 0 a 7 não existem.

Para copiar esse array esparso use este método

  1. Imprimir o antigo array:

    $ oldarr[8]=1234
    $ declare -p oldarr
    declare -a oldarr=([8]="1234")
    
  2. Substitua o nome do array e capture a string:

    $ str=$(declare -p oldarr | sed 's/oldarr=/newarr=/')
    
  3. Eval a string criada, a nova matriz foi definida:

    $ eval "$str"
    $ declare -p newarr
    declare -a newarr=([8]="1234")
    
por 09.03.2017 / 03:29

Tags