Bash-Skripte absichern: set -e, Quoting und Eingabeprüfung

Fortgeschritten Praxis Veröffentlicht am 29. September 2026 von Gunter Herdrich
Illustration: Terminalfenster mit Lupe, davor ein Schild mit Häkchen, ein abgesperrter Papierkorb und ein Ordner

Es gibt ein Bash-Skript, das Internetgeschichte geschrieben hat: rm -rf "$VERZEICHNIS/"* mit einer Variablen, die leer war. Aus dem Befehl wurde rm -rf /*, und alles, was dem Benutzer gehörte, war weg. Das Skript hatte keinen Logikfehler. Es hatte eine ungeprüfte Variable. Die meisten Skripte, die Schaden anrichten, tun das aus demselben Grund: Sie laufen weiter, wenn etwas fehlt, und sie behandeln Eingaben so, als wären sie immer das, was der Autor erwartet hat.

Dieser Beitrag zeigt dir drei Gewohnheiten, die den größten Teil dieser Fehler verhindern. Die Grundlagen zu Variablen, Schleifen und Funktionen findest du im Tutorial Bash Script schreiben. Hier geht es darum, dass diese Skripte nichts kaputt machen.

Gewohnheit 1: set -euo pipefail

Die erste Zeile nach dem Shebang sollte in jedem Skript set -euo pipefail lauten. Das -e beendet das Skript, sobald ein Befehl fehlschlägt, statt mit kaputten Zwischenergebnissen weiterzulaufen. Das -u macht die Verwendung einer nicht gesetzten Variablen zum Fehler. Eine Variable, die gesetzt, aber leer ist, lässt -u allerdings durch, und genau das war beim rm -rf aus dem Anfang der Fall. Dagegen hilft "${VERZEICHNIS:?}": Ist die Variable leer oder nicht gesetzt, bricht die Bash an dieser Stelle mit einer Fehlermeldung ab. Das -o pipefail sorgt dafür, dass eine Pipe als fehlgeschlagen gilt, wenn irgendein Glied scheitert, nicht nur das letzte. Es gibt Fälle, in denen -e im Weg steht, etwa bei grep, das ohne Treffer mit Fehlercode endet. Dann hängst du || true an den Befehl, bewusst und sichtbar, statt den Schutz für das ganze Skript abzuschalten.

Gewohnheit 2: Alles in Anführungszeichen

Die Bash trennt Wörter an Leerzeichen, und ein Dateiname mit Leerzeichen wird ohne Anführungszeichen zu zwei Argumenten. cp $datei $ziel funktioniert mit „bericht.txt“ und scheitert mit „bericht final.txt“. Die Regel ist einfach: Jede Variable, jeder Befehlsersatz steht in doppelten Anführungszeichen, "$datei", "$(date)", "${liste[@]}". Ausnahmen gibt es fast nie, und wer sie zu kennen glaubt, irrt meistens. Für Dateinamen mit Zeilenumbrüchen, die es tatsächlich gibt, nutzt du find -print0 mit xargs -0 oder while read -r -d "". Besonders heikel wird das, wenn Dateinamen oder Eingaben von außen kommen: Dann entscheidet ein Fremder, was in der Variablen steht. Wer sich generell für IT-Sicherheit im Alltag interessiert, findet im Kuketz-Blog viel Lesestoff.

Gewohnheit 3: Eingaben prüfen, bevor sie wirken

  • Argumente zählen: if [ "$#" -ne 2 ]; then echo "Aufruf: skript QUELLE ZIEL"; exit 1; fi am Anfang jedes Skripts mit Parametern.
  • Existenz prüfen: [ -d "$quelle" ] für Verzeichnisse, [ -f "$datei" ] für Dateien, [ -n "$variable" ] für nicht leere Werte.
  • Nie Benutzereingaben direkt in eval oder in einen Befehlsstring einsetzen. Was von außen kommt, ist Text, kein Code.
  • Pfade absichern: Vor jedem rm oder mv prüfen, dass die Variable nicht leer und nicht / ist. Eine Zeile Prüfung gegen einen leeren Rechner.

Werkzeuge, die Fehler vor dir finden

shellcheck ist ein Programm, das Bash-Skripte liest und typische Fehler meldet: fehlende Anführungszeichen, ungesetzte Variablen, unsichere Konstrukte. Es liegt in den Paketquellen aller großen Distributionen und sollte vor jedem Einsatz eines Skripts laufen. Dazu kommt bash -n skript.sh für einen Syntaxcheck ohne Ausführung und bash -x skript.sh, das jeden Befehl vor der Ausführung anzeigt. Wie teuer schlampige Datenverarbeitung werden kann, zeigt die Berichterstattung über Datenpannen auf netzpolitik.org immer wieder.

Trockenlauf und Rückweg

Ein Skript, das Dateien löscht oder verschiebt, sollte einen Trockenlauf-Schalter haben, der nur anzeigt, was passieren würde: Eine Variable DRY_RUN, die vor jedem destruktiven Befehl geprüft wird. Und es sollte einen Rückweg geben: lieber in einen Papierkorb-Ordner verschieben als löschen, lieber ein Backup vor dem Überschreiben anlegen. Beides kostet drei Zeilen und hat schon mehr Daten gerettet als jede Wiederherstellungssoftware.

Fazit: Drei Zeilen, die alles ändern

set -euo pipefail am Anfang, Anführungszeichen um jede Variable, ${VAR:?} vor jedem rm: Das sind die drei Gewohnheiten, die aus einem Skript, das meistens funktioniert, eines machen, das nicht kaputt macht. Die Dateien aus dem Anfang wären mit ${VERZEICHNIS:?} noch da. Das ist die ganze Lektion.