Referenzieren von Bash-Array-Variablen aus einem anderen Array

8

Ich möchte ein Skript schreiben, um auf mehrere Arrays aus einem anderen Array zu verweisen, das die Variablennamen dieses Arrays enthält.

Hier ist mein Code bisher:

#!/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

Ich möchte, dass die Ausgabe beide Arrays (von der äußeren for-Schleife) durchsucht und die entsprechenden Zeichenfolgen in der inneren for-Schleife druckt, die Echo aufruft. Meine aktuelle Ausgabe zeigt mir nur:

array1
array2

Ich wäre dankbar für Hinweise dazu. Vielen Dank!

chnppp
quelle
Gibt es einen Grund, warum Sie nicht einfach tun können for b in "${array1[@]}" "${array2[@]}"; do ...; done?
Kusalananda
Ich möchte, dass die Anzahl der Arrays flexibel ist. Wenn ich später ein Array hinzufüge, füge ich es einfach zu array_names hinzu und lasse die Schleife sich darum kümmern.
chnppp
Ich denke, dies ist ein Fall für eine indirekte Expansion. Siehe stackoverflow.com/questions/8515411/… - aber im Grunde macht das Wechseln ${a[@]}zu ${!a}das, was Sie wollen (glaube ich).
Parkamark
1
@parkamark Nein, das gibt ihm nur das erste Element jedes Arrays. Und ${!a[@]}gibt eine Länge des Arrays an a.
Kusalananda
Ja, der Wechsel zu ${!a}gibt mir nur die ersten Elemente.
chnppp

Antworten:

7

Bash 4.3 und höher unterstützt "Namensreferenzen" oder Namensreferenzen (ein ähnliches Konzept gibt es in ksh93, aber der Umfang ist ärgerlich unterschiedlich ):

#!/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

Die Variable arrist ein nameref, der wie ein Alias ​​für die benannte Variable fungiert (die Variable mit dem Namen $ain diesem Beispiel).

Ohne namerefs besteht eine Lösung in früheren Bash-Versionen darin, ein neues Array zu erstellen, das alle Elemente aus den anderen Arrays enthält:

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

... ein bisschen wie das array_namesArray in der Frage, aber mit dem Inhalt aller Arrays, und dann iterieren "${all[@]}".

Es ist auch möglich zu verwenden eval, aber der resultierende Code sieht erstaunlich schrecklich aus.

Siehe Antwort glenn Jackman für eine Variante mit variabler indirection (eingeführt in seiner jetzigen Form mit Bash Version 2).

Kusalananda
quelle
3

@Kusalananda hat die beste Antwort für aktuelle Versionen von Bash. Für frühere Versionen können Sie eine indirekte Variable verwenden:

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

Siehe den 4. Absatz von https://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion

Glenn Jackman
quelle
0

Als Variation dessen, was gesagt wurde:

#!/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

Zugriff auf die Elemente stattdessen über den Index

chris
quelle