bash: Verwendung Nullzeichen-separierter Dateinamen

von Hubert Schmid vom 2012-10-21

Shell-Skripte eignen sich gut, um wiederkehrende Aufgaben einfach und schnell zu automatisieren. In den meisten Fällen funktionieren die Skripte wie gewünscht. Schwierig wird es nur, wenn die Skripte auch robust gegenüber ungewöhnlichen Eingaben sein sollen. Ein Beispiel:

find /tmp/ -type f -mmin +60 | while read pathname; do rm -- "$pathname" done

Das Skript-Fragment soll temporäre Dateien löschen, die über eine Stunde nicht verändert wurden. Solange die Dateinamen nicht ungewöhnlich sind, tut das Skript auch was es soll. Kommen in den Dateinamen hingegen Steuer- oder Leerzeichen vor, so können noch ganz andere Dinge passieren. Existiert beispielsweise eine Datei, die wie folgt angelegt wird, so wird durch obiges Skript leider auch die zentrale Passwort-Datei gelöscht.

mkdir -p $'/tmp/\n/etc' touch $'/tmp/\n/etc/passwd'

Das erste Problem ist, dass die meisten unixoiden Betriebssysteme in Dateinamen alle Zeichen (genauer Oktetts) bis auf das Nullzeichen zulassen. Das zweite Problem ist, dass die bash so wie die meisten GNU-Programme den Umgang mit solchen Zeichenketten nicht gerade einfach machen.

Die beiden bekanntesten Ausnahmen sind vermutlich find und xargs. Ersteres fügt mit der Option -print0 die einzelnen Dateipfade mit Nullzeichen statt Zeilenumbrüchen zusammen, und Zweiteres spaltet den Eingabestrom mit der Option -0 entsprechend wieder auf.

Ein weiterer, wichtiger Vertreter mit Unterstützung für Nullzeichen-separierte Dateinamen ist tar. Durch die Kombination der Optionen --null, --no-unquote und -T werden die Pfade der zu archivierenden Dateien aus einer Datei beziehungsweise einem Eingabestrom gelesen und an den Nullzeichen aufgetrennt. So erstellt beispielsweise der folgende Befehl ein Archiv mit allen Dateien, die in den letzten 60 Minuten geändert wurden.

tar -C /home/ -acf /path/to/backup.tgz \ --null --no-unquote \ -T <( cd /home/ && find -type f -mmin -60 -print0 )

Darüber hinaus ist die Menge der Kommandozeilenprogramme mit expliziter Unterstützung für Nullzeichen-separierte Dateipfade überschaubar. Erwähnenswert sind noch grep, sort und uniq, die jeweils die Option -z kennen, um auf Nullzeichen-separierten Zeichenfolgen statt auf Zeilen zu arbeiten.

Im Zweifelsfall muss man auf die bash zurückgreifen. Die Unterstützung ist zwar nicht gut, aber die beiden wesentlichen Dinge funktionieren: Ein Nullzeichen-separierter Eingabestrom lässt sich wie im folgenden Skript-Fragment mit read aufteilen. Die Ausgabe lässt sich beispielsweise mit echo realisieren, wobei man zuerst den Dateipfad mit den Optionen -ne ausgibt und das abschließende Nullzeichen separat mit echo -n '\0'.

find /tmp/ -type f -mmin +60 -print0 | while IFS= read -r -d '' pathname; do rm -- "$pathname" done

Man kann also bash-Skripte schreiben, die robust gegenüber merkwürdigen Zeichen in Dateipfaden sind. Doch einfach macht es die bash dem Entwickler nicht, was sehr bedauerlich ist. Denn ohne gute Unterstützung werden die meisten Skripte auch weiterhin anfällig sein – sei es aus Bequemlichkeit der Entwickler oder mangels nicht-funktionaler Anforderungen.