Die `mapfile`-Funktion (auch bekannt als `readarray`) in der Bash ist ein mächtiges Werkzeug, um Daten aus einer Datei oder einem Standard-Input in ein Array zu laden. Das ermöglicht eine effiziente Verarbeitung von Textdaten innerhalb von Shell-Skripten. Während die Grundfunktionalität von `mapfile` relativ einfach ist, bietet die Option `-C` eine erweiterte Kontrollmöglichkeit, die oft übersehen wird. In diesem Artikel tauchen wir tief in die Option `-C` ein, erklären ihre Funktionsweise im Detail und zeigen anhand praktischer Beispiele, wie sie effektiv eingesetzt werden kann.
Was ist `mapfile` und wozu dient es?
Bevor wir uns der Option `-C` widmen, ist es wichtig, die Grundlagen von `mapfile` zu verstehen. `mapfile` liest Zeilen von einer Quelle (standardmäßig Standard-Input) und speichert sie als Elemente eines Arrays. Hier ist ein einfaches Beispiel:
#!/bin/bash
mapfile -t my_array < my_file.txt
# Nun enthält my_array die Zeilen aus my_file.txt als einzelne Elemente.
# Wir können die Elemente so ausgeben:
for i in "${!my_array[@]}"; do
echo "Zeile $i: ${my_array[$i]}"
done
In diesem Beispiel:
- `-t` entfernt abschließende Zeilenumbrüche von jeder Zeile. Dies ist oft erwünscht, um sauberere Daten zu erhalten.
- `my_array` ist der Name des Arrays, in dem die Daten gespeichert werden.
- `< my_file.txt` leitet den Inhalt der Datei `my_file.txt` an `mapfile` weiter.
Die Option `-C` im Detail
Die Option `-C` (Callback) ermöglicht es, vor dem Lesen jeder `count`-ten Zeile eine benutzerdefinierte Bash-Funktion aufzurufen. Das eröffnet eine Vielzahl von Möglichkeiten zur Vorverarbeitung, Filterung oder Protokollierung der Daten, bevor sie im Array gespeichert werden. Die Syntax lautet:
mapfile -C 'Befehl oder Funktion' -c count array_name < Quelle
Hierbei gilt:
- `’Befehl oder Funktion’` ist der Name des Befehls oder der Bash-Funktion, die aufgerufen werden soll.
- `-c count` gibt an, nach wie vielen Zeilen der Befehl oder die Funktion aufgerufen werden soll.
- `array_name` ist der Name des Arrays.
- `< Quelle` ist die Datenquelle (Datei oder Standard-Input).
Die aufgerufene Funktion oder der Befehl erhält die Indexnummer der aktuell gelesenen Zeile als Argument. Es ist wichtig zu beachten, dass die Zählung bei 0 beginnt.
Praktische Beispiele für die Verwendung von `-C`
Um die Verwendung von `-C` besser zu verstehen, betrachten wir einige praktische Beispiele.
Beispiel 1: Protokollierung jeder gelesenen Zeile
Dieses Beispiel protokolliert jede gelesene Zeile in eine Datei. Es kann hilfreich sein, um den Fortschritt des `mapfile`-Prozesses zu überwachen.
#!/bin/bash
log_line() {
echo "Verarbeite Zeile: $1" >> logfile.txt
}
mapfile -C log_line -c 1 -t my_array < my_file.txt
echo "Daten erfolgreich in das Array geladen."
In diesem Beispiel wird die Funktion `log_line` nach dem Lesen jeder Zeile (`-c 1`) aufgerufen. Die Funktion schreibt die aktuelle Zeilennummer in die Datei `logfile.txt`. Beachten Sie, dass das Argument `$1` innerhalb der Funktion `log_line` die Zeilennummer enthält.
Beispiel 2: Filtern von Zeilen basierend auf einem Muster
Dieses Beispiel filtert Zeilen, die ein bestimmtes Muster enthalten. Nur Zeilen, die das Muster erfüllen, werden in das Array aufgenommen.
#!/bin/bash
filter_line() {
# Globale Variable, um auf das Array zuzugreifen (keine gute Praxis, aber für das Beispiel ausreichend)
global my_array
line=$(head -n 1) # Liest die aktuelle Zeile aus dem Standard-Input
if [[ "$line" == *"error"* ]]; then
echo "$line" # Gib die Zeile aus, um sie von mapfile einzulesen.
else
echo "" # Gib eine leere Zeile aus, um das Einlesen zu verhindern.
fi
}
mapfile -C filter_line -c 1 -t my_array < my_file.txt
# Das Array my_array enthält nun nur Zeilen, die "error" enthalten.
echo "Gefilterte Daten:"
for i in "${!my_array[@]}"; do
echo "Zeile $i: ${my_array[$i]}"
done
Dieses Beispiel ist komplexer. Die Funktion `filter_line` liest die aktuelle Zeile vom Standard-Input (wichtig: `mapfile` leitet die Zeile nicht automatisch an die Funktion weiter, sie muss selbst gelesen werden). Dann prüft sie, ob die Zeile das Wort „error” enthält. Wenn ja, wird die Zeile an die Standardausgabe geschrieben (wichtig für `mapfile`), ansonsten wird eine leere Zeile geschrieben. `mapfile` fügt nur Zeilen, die *nicht* leer sind, dem Array hinzu. **ACHTUNG:** Die Kommunikation zwischen der Callback-Funktion und `mapfile` erfolgt über die Standardausgabe. Die Ausgabe der Funktion wird von `mapfile` als die (potenziell modifizierte) Zeile interpretiert, die in das Array aufgenommen werden soll.
Beispiel 3: Verarbeiten einer festen Anzahl von Zeilen gleichzeitig
Dieses Beispiel zeigt, wie man mit `mapfile -C` eine Funktion aufrufen kann, die nicht jede Zeile, sondern z.B. alle fünf Zeilen auf einmal verarbeitet. Dies ist nützlich, wenn die Verarbeitung von Daten in Batches effizienter ist.
#!/bin/bash
process_batch() {
echo "Verarbeite Batch ab Zeile: $1"
# Hier können Sie komplexere Operationen auf den Zeilen $1, $1+1, $1+2, $1+3 und $1+4 durchführen
# Zum Beispiel:
# head -n $(($1 + 5)) my_file.txt | tail -n 5 | ...
}
mapfile -C process_batch -c 5 -t my_array < my_file.txt
echo "Daten verarbeitet."
In diesem Fall wird die Funktion `process_batch` alle fünf Zeilen aufgerufen. Die Funktion erhält die Startzeilennummer des Batches als Argument. Innerhalb der Funktion kann man dann die benötigten fünf Zeilen aus der Eingabedatei extrahieren und verarbeiten.
Wichtige Hinweise und Best Practices
- Kommunikation über Standardausgabe: Wie bereits erwähnt, kommuniziert die Callback-Funktion mit `mapfile` über die Standardausgabe. Alles, was die Funktion auf die Standardausgabe schreibt, wird von `mapfile` als die (potenziell modifizierte) Zeile interpretiert, die in das Array aufgenommen werden soll.
- Vermeiden Sie Seiteneffekte: Idealerweise sollte die Callback-Funktion die Daten möglichst wenig verändern (außer Filtern und einfache Transformationen) und keine unerwarteten Seiteneffekte haben. Komplexere Operationen sollten nach dem Einlesen der Daten erfolgen.
- Fehlerbehandlung: Achten Sie auf eine robuste Fehlerbehandlung innerhalb der Callback-Funktion. Unbehandelte Fehler können zu unerwartetem Verhalten führen.
- Globale Variablen: Der Zugriff auf globale Variablen innerhalb der Callback-Funktion sollte vermieden werden. Stattdessen sollten Sie die Daten über die Standardausgabe zurückgeben und nach dem Einlesen verarbeiten.
- Performance: Das Aufrufen einer externen Funktion für jede (oder alle paar) Zeile(n) kann die Performance beeinträchtigen, insbesondere bei großen Dateien. Überlegen Sie, ob alternative Methoden (z.B. `awk`, `sed`) effizienter sein könnten.
Fazit
Die Option `-C` von `mapfile` bietet eine flexible Möglichkeit, Daten während des Einlesens vorzuverarbeiten. Durch das Aufrufen einer benutzerdefinierten Funktion oder eines Befehls nach einer bestimmten Anzahl von Zeilen können komplexe Filter- und Transformationsoperationen durchgeführt werden. Es ist jedoch wichtig, die Funktionsweise der Option `-C` genau zu verstehen und die Best Practices zu beachten, um unerwartetes Verhalten und Performance-Probleme zu vermeiden. Mit dem richtigen Verständnis ist `-C` ein wertvolles Werkzeug in Ihrem Bash-Werkzeugkasten.