Assoziative Arrays
Im Gegensatz zum allgemeinen Vorurteil eignet sich die Bash nicht nur für kleinere Wrapper, die das Starten von Programmen vereinfachen. Ein Mythos lautet zum Beispiel, dass Bash-Skripte zu viele Prozesse erzeugen und deswegen nur mäßig performant sind. Doch viele einfache Anwendungen von »sed«, »grep«, »basename« oder »dirname« sind schon seit Langem nicht mehr notwendig - die Bash erledigt solche Aufgaben mit internen Mitteln so performant wie andere Skriptsprachen auch.
Trotzdem werfen Bash-Programmierer immer neidvolle Blicke auf Perl und Python, denn dort gibt es vielfältigere Datenstrukturen. Mit der Version 4 hat die Bash nun endlich zu den eindimensionalen auch assoziative Arrays bekommen. Das allein ist schon ein Grund, auf die neue Version umzusteigen, denn damit lassen sich viele Probleme eleganter lösen. In assoziativen Arrays dürfen Entwickler als Index nicht nur einen Zahlenwert, sondern einen beliebigen String verwenden. Ein Beispiel zeigt Listing 1.
Listing 2 präsentiert einen realeren Anwendungsfall. Ein Skript sortiert Dateien aufgrund von Eigenschaften, zum Beispiel anhand des Erstelldatums, in Verzeichnisse ein. Anschließend soll es jedes Verzeichnis weiterverarbeiten. Das Skript hat aber zwei grundsätzliche Probleme: Einmal funktioniert es nicht mit Verzeichnissen, die Leerzeichen enthalten, zum zweiten verarbeitet es eventuell einzelne Verzeichnisse mehrfach.
Es gibt hier verschiedene Lösungsansätze. Unter Bash 3.2 legt der Programmierer die Verzeichnisse etwa gequotet in einem String oder in einem Array ab. Doppelte Verarbeitung verhindert er im ersten Fall durch eine Suche im String und im zweiten Fall durch eine langsame lineare Suche im Array. Beide Ansätze sind nicht wirklich elegant.
Die Bash 4 erledigt das viel einfacher (siehe Listing 3). Der Verzeichnisname dient als Schlüssel, der eigentliche Wert interessiert nicht. Ab Zeile 12 iteriert die Schleife über alle Schlüssel, das spezielle Konstrukt mit dem Klammeraffen in doppelten Hochkommta sorgt wie überall in der Bash dafür, dass die Shell alle Werte als einzelne Tokens weiterverarbeitet - die Lösung funktioniert also auch mit Leerzeichen in den Zielverzeichnissen.
|
Listing 1: Programmieren mit |
|---|
01 #!/bin/bash
02
03 declare -A name
04
05 name["Linus"]="Torvalds"
06 name["Bill"]="Gates"
07 name["Steve"]="Jobs"
08 name["George W."]="Bush"
09
10 # alle Werte ausgeben:
11 echo "Werte: ${name[@]}"
12
13 # alle Schlüssel ausgeben:
14 echo "Schlüssel: ${!name[@]}"
15
16 # Zugriff auf einzelne Werte
17 for v in "${!name[@]}"; do
18 echo "$v ${name[$v]}"
19 done
|
|
Listing 2: Strings in Listen |
|---|
01 #!/bin/bash 02 03 dirs="" 04 05 for f in "$@"; do 06 d=`getDir "$f"` 07 mkdir -p "$d" 08 mv -f "$f" "$d" 09 dirs="$dirs $d" 10 done 11 12 for d in $dirs; do 13 createIndex "$d" 14 done |
|
Listing 3: Assoziative |
|---|
01 #!/bin/bash
02
03 declare -A dirs
04
05 for f in "$@"; do
06 d=`getDir "$f"`
07 mkdir -p "$d"
08 mv -f "$f" "$d"
09 dirs[$d]=1
10 done
11
12 for d in "${!dirs[@]}"; do
13 createIndex "$d"
14 done
|
Dateiverarbeitung
Ein typisches Programmiermuster enthält Listing 4. Die While-Schleife in den Zeilen 5 bis 8 liest der Reihe nach die Zeilen aus einer Eingabedatei und speichert sie in einem Array. Das Konstrukt kommt immer wieder vor, doch ist es leider fehlerhaft. Endet die letzte Zeile nicht mit einem Zeilenvorschub, also einem Newline-Zeichen, speichert die Schleife die Zeile nicht. Das eingebaute Kommando »read« führt nämlich erst nach abgeschlossener Zeile die Zuweisung aus. Die neue Bash-Version erspart nicht nur Tipparbeit, sondern erzielt auch eine saubere Implementation. Statt der While-Schleife reicht eine einzige Zeile (siehe Zeile 4 in Listing 5). Der Befehl »mapfile« hat zusätzlich als Alias »readarray«, was diesen Zweck noch besser beschreibt.
Der »mapfile«-Befehl ist noch viel leistungsfähiger. In der neuen Manpage oder einfacher mit »help mapfile« auf der Kommandozeile bekommt der neugierige Anwender alle notwendigen Informationen. Der Befehl kann nicht nur mehrere Zeilen am Stück einlesen, sondern diese Zeilen auch häppchenweise über eine Callback-Funktion weiterverarbeiten.
Leider haben die Entwickler die Funktion unglücklich implementiert. Bash 4 ruft den Callback vor dem Einlesen auf und nicht danach. Damit erhält das Skript einen Rückruf, bevor die Shell etwas gelesen hat, verliert aber einen nach der letzten Zeile. Dennoch ist die Funktion nützlich, um ganze Dateien einzulesen.
|
Listing 4: Klassisch Dateien in |
|---|
01 #!/bin/bash 02 03 inputFile="$1" 04 i=0 05 while read line; do 06 lines[$i]="$line" 07 let i++ 08 done < "$inputFile" 09 10 # Weiterverarbeitung des Arrays lines ... |
|
Listing 5: Bash 4 liest Dateien |
|---|
01 #!/bin/bash 02 03 inputFile="$1" 04 mapfile -n 0 lines < "$inputFile" 05 06 # Weiterverarbeitung des Arrays lines ... |
Diesen Artikel als PDF kaufen
Express-Kauf als PDF
Umfang: 4 Heftseiten
Preis € 0,99
(inkl. 19% MwSt.)
Als digitales Abo
Weitere Produkte im Medialinx Shop »
Versandartikel
Onlineartikel
Alle Rezensionen aus dem Linux-Magazin
- Buecher/07 Bücher über 3-D-Programmierung sowie die Sprache Dart
- Buecher/06 Bücher über Map-Reduce und über die Sprache Erlang
- Buecher/05 Bücher über Scala und über Suchmaschinen-Optimierung
- Buecher/04 Bücher über Metasploit sowie über Erlang/OTP
- Buecher/03 Bücher über die LPI-Level-2-Zertifizierung
- Buecher/02 Bücher über Node.js und über nebenläufige Programmierung
- Buecher/01 Bücher über Linux-HA sowie über PHP-Webprogrammierung
- Buecher/12 Bücher über HTML-5-Apps sowie Computer Vision mit Python
- Buecher/11 Bücher über Statistik sowie über C++-Metaprogrammierung
- Buecher/10 Bücher zu PHP-Webbots sowie zur Emacs-Programmierung
Insecurity Bulletin
Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...





