Arrays haben wir im Kapitel Arrays eingeführt. Dieses Dokument zeigt dir weitere Möglichkeiten, Arrays zu verwenden.
Im vorigen Array-Kapitel hast du "${myarray[@]}" mit dem @-Index gesehen, womit das Array in die einzelnen Elemente expandiert wird.
Manchmal möchtest du aber alle Elemente zu einem einzigen String zusammenfügen.
Dafür verwendest du den *-Index:
echo "${myarray[*]}"
Die Expansion muss in doppelte Anführungszeichen eingeschlossen werden.
Bash verwendet das erste Zeichen der eingebauten Variable IFS als Trennzeichen.
Standardmäßig besteht $IFS aus Leerzeichen, Tabulator und Zeilenumbruch.
myarray=(one two three four)
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one two three four"
Wir können die Variable IFS anpassen, um ein anderes Trennzeichen zu verwenden:
myarray=(one two three four)
IFS=","
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one,two,three,four"
join() {
local IFS=$1
shift
local elements=("$@")
echo "${elements[*]}"
}
join ":" "${myarray[@]}" # note, the "@" index
# => "one:two:three:four"
Wenn wir IFS in der Funktion lokalisieren, müssen wir den alten Wert nicht sichern und im globalen Gültigkeitsbereich nicht wiederherstellen.
Als Verfeinerung: Der spezielle Parameter "$*" hat in Anführungszeichen dieselbe Funktionalität, sodass wir keine Kopie der Argumente der Funktion sichern müssen:
join() {
local IFS=$1
shift
echo "$*"
}
(IFS=","; echo "${myarray[*]}")
Die runden Klammern erzeugen eine Subshell (eine Kopie der aktuellen Shell). Wenn die Befehle in den Klammern fertig sind, beendet sich die Subshell und die geänderte Variable IFS verschwindet.
Beachte, dass das nicht funktioniert: IFS="," echo "${myarray[*]}". Die Parameterexpansion erfolgt zuerst, bevor die Shell die geänderte Variable IFS auf den Befehl echo anwendet.
Du hast vielleicht schon die Parameterexpansion "${variable:offset:length}" gesehen, die zu einem Teilstring des Werts der Variable expandiert.
Dasselbe können wir mit Arrays machen, um einen Ausschnitt des Arrays zu expandieren.
myarray=(one two three four)
subarray=("${myarray[@]:0:2}")
declare -p subarray
# => declare -a subarray=([0]="one" [1]="two")
subarray=("${myarray[@]:1:3}")
declare -p subarray
# => declare -a subarray=([0]="two" [1]="three" [2]="four")
Wenn du den Längen-Teil weglässt, bedeutet das „vom Offset bis zum Ende des Arrays“:
subarray=("${myarray[@]:2}")
declare -p subarray
# => declare -a subarray=([0]="three" [1]="four")
Das ist nicht so unkompliziert wie in anderen Sprachen, die du vielleicht kennst. Es gibt zwei grundlegende Techniken, ein Array an eine Funktion zu übergeben.
Bei der ersten Technik übergibst du alle Werte des Arrays und sammelst sie in der Funktion in einem lokalen Array.
myfunc() {
local array_copy=("$@")
# do stuff with array_copy
declare -p array_copy
}
array_original=(11 22 33 44)
myfunc "${array_original[@]}"
Das Array der Funktion enthält eine Kopie der Werte. Änderungen, die in der Funktion am Array vorgenommen werden, wirken sich nicht auf den äußeren Gültigkeitsbereich aus.
Diese Technik ähnelt eher der Möglichkeit der „Übergabe als Referenz“, die du aus anderen Sprachen kennst. Du übergibst den Namen des Arrays als String. Die Funktion erstellt eine lokale Variable mit dem Attribut „nameref“. Dieses lokale Array und das globale Array (dessen Namen wir übergeben haben) sind dasselbe Array.
myfunc() {
# note the `-n` option
local -n local_array=$1
# do stuff with local_array
for i in "${!local_array[@]}"; do
printf '%d => %s\n' "$i" "${local_array[i]}"
end
# we can mutate it
local_array+=(55 66 77)
}
array_original=(11 22 33 44)
myfunc "array_original"
# show the mutated array
declare -p array_original
# => declare -a array_original=([0]="11" [1]="22" [2]="33" [3]="44" [4]="55" [5]="66" [6]="77")
Namerefs funktionieren auch mit assoziativen Arrays und mit „skalaren“ Variablen (die einen String-Wert enthalten).
Innerhalb der Funktion ist declare -p local_array nicht besonders hilfreich.
Sie gibt nur declare -n local_array="array_original" aus.
Detaillierte Informationen über das Array bekommst du, wenn du den übergebenen Array-Namen untersuchst: declare -p "$1"
Achte darauf, dass das lokale Array einen anderen Namen hat als das übergebene Array. Der Code funktioniert trotzdem, aber er gibt Warnungen wie „circular name reference“ aus:
myfunc() {
local -n a=$1
local IFS=,
echo "${a[*]}"
}
# same name as the function's local variable
a=(one two three)
myfunc a
bash: local: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
one,two,three
In Shells, die nur dem POSIX-Standard entsprechen wollen (wie ash und dash), gibt es keine Arrays.
Am nächsten kommst du heran, wenn du die Positionsparameter verwendest.
$1, $2 usw."$@" werden sie in einzelne Elemente expandiert"$*" werden sie zu einem einzigen String zusammengefügt$#
Mit dem Befehl set weist du ihnen Werte zu:
set -- one two three
set -- "$@" four
for item in "$@"; do
echo "do something with $item"
done
Wenn dein Ziel ist, „portable“ Shell-Skripte zu schreiben, verwendest du die Positionsparameter, um eine „Liste“ von Werten zu speichern.