Referenciando variáveis ​​do array bash de outro array

8

Eu quero escrever um script para referenciar várias matrizes de outra matriz que contém os nomes de variáveis ​​dessa matriz.

Aqui está o meu código até agora:

#!/bin/bash
array1=('array1string1' 'array1string2')
array2=('array2string1' 'array2string2')

array_names=('array1' 'array2')

for a in ${array_names[@]}
do
        for b in ${a[@]}
        do
                echo $b
        done
done

Gostaria que a saída digitalizasse as duas matrizes (a partir do loop for externo) e imprimisse as respectivas strings no loop for interno que chama eco. Minha saída atual está apenas me mostrando:

array1
array2

Eu ficaria grato por qualquer indicação sobre isso. Obrigado!

chnppp
fonte
Existe alguma razão pela qual você não pode simplesmente fazer for b in "${array1[@]}" "${array2[@]}"; do ...; done?
Kusalananda
Eu gostaria que o número de matrizes fosse flexível. Portanto, se eu adicionar uma matriz posteriormente, eu a adicionaria a array_names e deixaria o loop cuidar disso.
chnppp
Eu acho que esse é um caso de expansão indireta. Veja stackoverflow.com/questions/8515411/… - mas, basicamente, mudar ${a[@]}para ${!a}o que você deseja (eu acho).
parkamark
1
@parkamark Não, isso apenas fornece a ele o primeiro elemento de cada array. E ${!a[@]}fornece um comprimento da matriz a.
Kusalananda
Sim, mudar para ${!a}é apenas me dar os primeiros elementos.
chnppp

Respostas:

7

O Bash 4.3 e versões posteriores suportam "referências de nomes" ou namerefs (existe um conceito semelhante ksh93, mas o escopo é irritantemente diferente ):

#!/bin/bash

array1=('array1string1' 'array1string2')
array2=('array2string1' 'array2string2')

array_names=('array1' 'array2')

for a in "${array_names[@]}"; do
    declare -n arr="$a"

    for b in "${arr[@]}"; do
        echo "$b"
    done
done

A variável arré um nameref que age como um alias para a variável nomeada (a variável com nome $aneste exemplo).

Sem namerefs, nas versões anteriores do Bash, uma solução seria criar uma nova matriz que contenha todos os elementos das outras matrizes:

all=( "${array1[@]}" "${array2[@]}" )

... um pouco como a array_namesmatriz da pergunta, mas com o conteúdo de todas as matrizes e, em seguida, iterar "${all[@]}".

Também é possível usar eval, mas o código resultante parece surpreendentemente terrível.

Veja a resposta de glenn jackman para uma variação com indireção variável (introduzida em sua forma atual com a versão 2 do Bash).

Kusalananda
fonte
3

@Kusalananda tem a melhor resposta para versões recentes do bash. Para versões anteriores, você pode usar uma variável indireta:

for a in ${array_names[@]}; do 
    tmp="${a}[@]"
    for b in "${!tmp}"; do echo "$b"; done     # or: printf "%s\n" "${!tmp}"
done

Consulte o quarto parágrafo de https://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion

Glenn Jackman
fonte
0

Como uma variação do que foi dito:

#!/bin/bash

array1=('array1 string1' 'array1 string2')
array2=('array2 string1' 'array2 string2')
array_names=('array1' 'array2')

for (( i=0; i<${#array_names[@]}; i++ )); do

    declare -n arr="${array_names[i]}"

    for (( j=0; j<${#arr[@]}; j++ )); do
        echo "${arr[j]}"
    done

done

Acessando os elementos por índice

chris
fonte