Indirekte Rückgabe aller Elemente in einem Array

9

Die Bash-Manpage beschreibt die Verwendung von ${!a}, um den Inhalt der Variablen zurückzugeben, deren Name der Inhalt von ist a(eine Indirektionsebene).

Ich möchte wissen, wie alle Elemente in einem Array mit diesem zurückgegeben werden können, d. H.

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

kehrt zurück

one two three

Ich möchte für:

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

das gleiche zurückzugeben. Leider nicht, sondern kehrt 0stattdessen zurück.

Aktualisieren

Angesichts der Antworten stelle ich jetzt fest, dass mein Beispiel zu einfach war, da natürlich so etwas wie:

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

Wird genau das erreichen, was ich gesagt habe.

Also, hier ist was ich versucht habe zu tun:

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

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

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

Das sorgfältige Lesen der Bash-Manpage zeigt natürlich, dass dies nicht wie erwartet funktioniert, da die letzte Zeile einfach die Indizes des "Arrays" zurückgibt $_LIST(überhaupt kein Array).

In jedem Fall sollte Folgendes die Arbeit erledigen (wie bereits erwähnt):

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

oder ... (der Weg, den ich schließlich gegangen bin):

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

Vorausgesetzt natürlich, dass Elemente keine Leerzeichen enthalten.

Eric Smith
quelle
Fügen Sie [@]dem Zeiger hinzu _LIST="LIST_${whichone}[@]"und LIST=("${!_LIST}")kopieren Sie das Array mit. Es ist eine gute Idee, Variablennamen in Kleinbuchstaben zu verwenden, um Konflikte mit Umgebungsvariablen (Großbuchstaben) zu vermeiden.
Isaac

Antworten:

6

Ich denke, die Verwendung der indirekten Referenz der Bash-Variablen sollte wörtlich behandelt werden.

Z.B. Für Ihr ursprüngliches Beispiel:

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

Für das letzte reale Szenario glaube ich, dass der folgende Code die Arbeit erledigen würde.

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

Es ist besser, die Notation zu verwenden "${var[@]}", um die $IFSParametererweiterung nicht zu verfälschen. Hier ist der endgültige Code.

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
weynhamz
quelle
8

Sie müssen die Elemente explizit kopieren. Für ein indiziertes Array:

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

Für ein assoziatives Array (beachten Sie, dass dies ader Name der Arrayvariablen ist, nicht eine Variable, deren Wert der Name einer Arrayvariablen ist):

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

Wenn Sie den Variablennamen in einem Array haben, können Sie die Element-für-Element-Methode mit einem zusätzlichen Schritt verwenden, um die Schlüssel abzurufen.

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

(Achtung, der Code in diesem Beitrag wurde direkt in einen Browser eingegeben und nicht getestet.)

Gilles 'SO - hör auf böse zu sein'
quelle
Sie haben völlig Recht, aber das löst mein Problem leider nicht (mein Beispiel war zu simpel). Ich habe ein Update bereitgestellt, um meine Absicht klarer zu machen.
Eric Smith
@ Eric Ich denke in ksh / bash brauchst du eval in diesem Stadium. Siehe meine Bearbeitung.
Gilles 'SO - hör auf böse zu sein'
+1, aber gemäß Ihrem Haftungsausschluss funktioniert dies nicht ganz (das letzte Stück Code), sondern erfordert nur ein paar geringfügige Anpassungen. Insbesondere sollte es sich \${!$name[@]}in der ersten Zeile befinden, damit die erste Erweiterung nur den Namen '$ name' hat und die ${!a[@]}für die Auswertung gespeichert wird, und dasselbe in der for-Schleife mit \${$name}. Die beiden anderen Backslashes vor '$ k' sind dort ebenfalls nicht unbedingt erforderlich.
krb686
2

${!b[*]}wird auf die im Array verwendeten Indizes erweitert b.

Was Sie möchten, muss in zwei Schritten erledigt werden, um zu evalhelfen : eval echo \${$b[*]}. (Beachten Sie das, \was sicherstellt, dass der erste $den ersten Schritt, die variable Erweiterung, besteht und erst im zweiten Schritt um erweitert wird eval.)

Laut Parameter wird Expansion !sowohl für die indirekte Erweiterung ( {!a}) als auch für Namen verwendet, die dem Präfix ( ${!a*}) und der Liste der Array-Schlüssel ( ${!a[*]}) entsprechen. Da die Liste der Array-Schlüssel dieselbe Syntax hat wie Ihre beabsichtigte indirekte Erweiterung + Array-Element-Erweiterung, wird die letztere nicht so unterstützt, wie sie ist.

Mann bei der Arbeit
quelle
2
${!a}wird auf den Wert der Variablen erweitert, deren Name lautet $a. Dies wird im Handbuch im Abschnitt, der mit „Wenn das erste Zeichen des Parameters ein Ausrufezeichen ( !) ist, wird eine Ebene der variablen Indirektion eingeführt“ ziemlich knapp beschrieben .
Gilles 'SO - hör auf böse zu sein'
Ja - @Gilles ist richtig, aber @manatwork, in der zweiten Lesung, habe ich festgestellt, dass dies etwas ${!mehrdeutig ist, da das Verhalten anders ist, wenn es sich um ein Array handelt, mit dem Sie es zu tun haben.
Eric Smith
@ Gilles Sie haben Recht mit diesem Satz, aber leider gilt er nicht als "Die Ausnahmen hiervon sind die unten beschriebenen Erweiterungen von $ {! Prefix *} und $ {! Name [@]}." Aber meine Antwort ist sicherlich ein mehrdeutiges Durcheinander, also werde ich sie bearbeiten.
Manatwork
0

Um indirekt auf Arrays zuzugreifen, fügen Sie einfach [@]die indirekte Variable hinzu b=a[@].

Wenn diese Variablen gesetzt sind:

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

Dann wird dies funktionieren:

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

Oder einfach:

echo "${!b}"

Ein solches Array könnte wie folgt kopiert werden:

newArr=( "${!b}" )

Und dann gedruckt mit:

declare -p newArr

In einem Skript:

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

Natürlich kopieren alle oben genannten Elemente ein nicht spärliches Array. Eine, in der alle Indizes einen Wert haben.

spärliche Arrays

Ein Array mit geringer Dichte kann nicht definierte Elemente enthalten.
Definiert zum Beispiel a[8]=1234ein Element und in Bash existieren 0 bis 7 nicht.

Verwenden Sie diese Methode, um ein solches spärliches Array zu kopieren

  1. Drucken Sie das alte Array:

    $ oldarr[8]=1234
    $ declare -p oldarr
    declare -a oldarr=([8]="1234")
  2. Ersetzen Sie den Namen des Arrays und erfassen Sie die Zeichenfolge:

    $ str=$(declare -p oldarr | sed 's/oldarr=/newarr=/')
  3. Bei der Bewertung der so erstellten Zeichenfolge wurde das neue Array definiert:

    $ eval "$str"
    $ declare -p newarr
    declare -a newarr=([8]="1234")
Isaac
quelle