bash check if directory exists

bash check if directory exists

Ich stand vor drei Jahren in einem klimatisierten Serverraum in Frankfurt, während mein Telefon ununterbrochen vibrierte. Ein Junior-Admin hatte ein Aufräumskript gestartet, das eigentlich nur alte Log-Verzeichnisse löschen sollte. Das Problem? Er verließ sich auf eine unsaubere Prüfung der Pfade. Das Skript dachte, ein Verzeichnis sei nicht vorhanden, sprang in den Fehlerzweig und löschte durch eine unglückliche Variablenverkettung stattdessen den Mount-Punkt der Kundendatenbank. Der finanzielle Schaden durch den Ausfall belief sich am Ende des Tages auf knapp 40.000 Euro. Das alles passierte, weil das einfache Bash Check If Directory Exists falsch implementiert wurde. Wer glaubt, eine einfache eckige Klammer würde alle Probleme lösen, hat noch nie ein Skript unter Zeitdruck auf einem Produktivsystem mit verschiedenen Dateisystemen laufen lassen.

Die Illusion der einfachen eckigen Klammer

In fast jedem Online-Forum liest man, dass man einfach [ -d "$DIR" ] schreiben soll. Das klingt logisch. Das Problem ist, dass viele Leute den Unterschied zwischen einem Verzeichnis, einem Symlink auf ein Verzeichnis und einer Datei gar nicht auf dem Schirm haben. Ich habe Projekte gesehen, bei denen Skripte hängen blieben, weil sie versuchten, in ein Verzeichnis zu schreiben, das in Wirklichkeit eine Pipe oder ein blockorientiertes Gerät war.

Wenn du nur prüfst, ob etwas existiert, ohne zu wissen, was es ist, baust du dir eine Zeitbombe. Ein Verzeichnis ist unter Linux technisch gesehen auch nur eine Datei, aber mit speziellen Attributen. Die meisten Anfänger vergessen die Anführungszeichen um die Variable. Sobald ein Leerzeichen im Pfad auftaucht – was in modernen Cloud-Umgebungen oder bei Nutzer-Uploads ständig passiert –, bricht dein Skript mit einem Syntaxfehler ab. Im schlimmsten Fall wird die Prüfung wahr, obwohl sie falsch sein sollte, weil die Bash den Ausdruck falsch zerteilt.

Bash Check If Directory Exists und das Problem mit den Berechtigungen

Es ist ein klassischer Denkfehler: Nur weil ein Verzeichnis existiert, heißt das noch lange nicht, dass dein Skript auch damit arbeiten kann. Ein häufiger Fehler, den ich bei Automatisierungslösungen sehe, ist die Annahme, dass eine erfolgreiche Prüfung den Weg ebnet.

Du prüfst, ob /data/uploads da ist. Die Prüfung ergibt „Ja“. Dein Skript versucht, eine Datei darin abzulegen und stirbt einen einsamen Tod durch „Permission Denied“. Warum? Weil das Verzeichnis root gehört und dein Skript unter einem eingeschränkten Web-User läuft. Ein Profi prüft niemals nur die Existenz. Er prüft die Lesbarkeit, die Schreibbarkeit und die Ausführbarkeit. Ohne das „x“-Bit auf einem Verzeichnis kannst du nicht einmal hineinwechseln, selbst wenn du den Namen kennst. Das kostet dich Stunden bei der Fehlersuche, wenn du nur auf die reine Existenz starrst.

Der Unterschied zwischen Existenz und Nutzbarkeit

Ich habe oft erlebt, wie Entwickler versuchen, Verzeichnisse auf NFS-Mounts zu prüfen. Wenn das Netzwerk laggt oder der Remote-Server weg ist, bleibt dein einfacher Testbefehl unter Umständen sekundenlang hängen. Das Skript blockiert, andere Prozesse stauen sich auf und plötzlich geht gar nichts mehr. Hier hilft kein einfacher Test, hier brauchst du Timeouts und eine Fehlerbehandlung, die nicht davon ausgeht, dass die Hardware immer sofort antwortet.

Warum Symlinks deine Logik zerstören

Ein weiterer Punkt, an dem viele scheitern, sind symbolische Verknüpfungen. Wenn du Bash Check If Directory Exists ausführst, verhält sich der Standardtest -d so, dass er dem Link folgt. Das kann gewollt sein, ist aber oft gefährlich. Stell dir vor, du willst ein Verzeichnis rekursiv löschen und landest durch einen Symlink plötzlich in einem ganz anderen Teil deines Systems.

Ich erinnere mich an einen Fall, bei dem ein Skript in einer Endlosschleife landete, weil ein Symlink im Verzeichnis auf das übergeordnete Verzeichnis verwies. Die Prüfung sagte immer wieder: „Ja, das ist ein Verzeichnis“, und das Skript grub sich tiefer und tiefer in den Verzeichnisbaum, bis der Arbeitsspeicher voll war. Man muss explizit entscheiden, ob man den Link prüfen will oder das Ziel. Wer das ignoriert, spielt russisches Roulette mit seiner Ordnerstruktur.

Der Vorher-Nachher-Vergleich in der Praxis

Schauen wir uns an, wie der typische Amateurcode aussieht und wie ich ihn heute schreibe, nachdem ich genug Lehrgeld bezahlt habe.

Früher sah man oft Code wie diesen: Ein Pfad wird in einer Variablen gespeichert, dann kommt die Abfrage mit if [ -d $MY_DIR ]. Wenn das Verzeichnis fehlt, wird es mit mkdir erstellt. Danach wird blindlings eine Datei hineinkopiert. Das Problem hierbei ist vielfältig. Wenn $MY_DIR leer ist, prüft die Bash plötzlich das aktuelle Verzeichnis. Wenn der Pfad Leerzeichen hat, stürzt das Skript ab. Wenn mkdir fehlschlägt, weil die Festplatte voll ist oder die Quoten erreicht sind, merkt das Skript es nicht und versucht trotzdem zu kopieren. Die Fehlermeldungen landen im Nirgendwo, und am nächsten Morgen wundert sich der Kunde, warum keine Backups da sind.

Heute sieht ein sicheres Vorgehen anders aus. Zuerst wird die Variable strikt in Anführungszeichen gesetzt. Vor der Prüfung wird sichergestellt, dass die Variable überhaupt einen Wert hat, um nicht versehentlich das Root-Verzeichnis zu manipulieren. Dann wird geprüft, ob der Pfad existiert. Falls er existiert, wird zusätzlich sichergestellt, dass es kein reguläres File ist, das nur so heißt wie unser Zielordner. Wenn das Verzeichnis erstellt werden muss, wird der Rückgabewert von mkdir sofort abgefangen. Erst wenn feststeht, dass das Verzeichnis existiert UND beschreibbar ist, geht die Arbeit los. Falls irgendetwas davon schiefgeht, bricht das Skript mit einer klaren Meldung nach stderr ab und liefert einen Exit-Code ungleich Null zurück. Das ist der Unterschied zwischen einem Skript, das man einmal benutzt, und einem, das jahrelang wartungsfrei auf einem Server läuft.

Das Rennen gegen die Zeit: Race Conditions

Ein Fehler, der vor allem in Umgebungen mit vielen parallelen Prozessen auftritt, ist die sogenannte Race Condition. Du prüfst, ob ein Verzeichnis existiert. Die Bash sagt dir „Nein“. In der Millisekunde zwischen deiner Prüfung und deinem nächsten Befehl erstellt jedoch ein anderer Prozess genau dieses Verzeichnis. Dein Skript versucht nun, es ebenfalls zu erstellen, und wirft einen Fehler.

Oder noch schlimmer: Du prüfst die Existenz, die Antwort ist „Ja“, aber direkt danach löscht ein Aufräum-Cronjob den Ordner. Dein Skript verlässt sich auf die veraltete Information der Prüfung. In der Bash-Programmierung ist es oft klüger, einfach eine Aktion zu versuchen und den Fehler abzufangen, anstatt vorher höflich zu fragen. Ein mkdir -p ist beispielsweise atomarer und sicherer als erst zu prüfen und dann zu handeln. Profis wissen, dass der Zustand des Dateisystems flüchtig ist. Wer sich zu sehr auf das Ergebnis einer Prüfung von vor einer Sekunde verlässt, hat schon verloren.

Globbing und versteckte Fallen

Ein Bereich, der oft unterschätzt wird, ist das Verhalten bei Wildcards. Wenn du prüfen willst, ob ein Verzeichnis existiert, das einem bestimmten Muster entspricht, zum Beispiel backup_*, dann wird die einfache Prüfung oft fehlschlagen, wenn mehr als ein Treffer gefunden wird. Die Bash expandiert die Wildcard, und dein Testbefehl bekommt plötzlich fünf Argumente statt einem. Er weiß nicht, was er damit anfangen soll, und gibt eine Fehlermeldung aus.

Ich habe gesehen, wie automatisierte Deployment-Skripte abgestürzt sind, weil nach dem zweiten Release plötzlich zwei Ordner da waren und die Prüfung nicht darauf ausgelegt war, mit einer Liste umzugehen. Man muss hier mit Schleifen arbeiten oder das Ergebnis der Expansion genau kontrollieren. Einfach zu hoffen, dass immer nur ein Ordner da ist, ist keine Strategie, sondern Wunschdenken.

Warum set -e dich nicht immer rettet

Viele verlassen sich auf set -e, um das Skript bei Fehlern sofort zu stoppen. Aber gerade bei der Prüfung von Verzeichnissen kann das nach hinten losgehen. Wenn du eine Prüfung in einer Weise schreibst, die einen negativen Exit-Code erzeugt, beendet sich dein Skript vielleicht genau dann, wenn es eigentlich nur einen alternativen Pfad einschlagen sollte. Man muss lernen, wie man Exit-Codes gezielt abfängt und steuert, anstatt sich auf globale Schalter zu verlassen, die man nicht vollends durchdringt.

💡 Das könnte Sie interessieren: gut zu hause angekommen

Der Realitätscheck

Erfolg in der Bash-Programmierung hat wenig mit dem Auswendiglernen von Flags zu tun. Es geht darum, paranoid zu sein. Wenn du denkst, ein Verzeichnis ist da, geh davon aus, dass es gerade gelöscht wird. Wenn du denkst, du hast Schreibrechte, geh davon aus, dass die Festplatte gerade auf Read-Only gesprungen ist, weil das Dateisystem korrupt ist.

In der echten Welt sind Skripte keine isolierten Logikrätsel. Sie interagieren mit Hardware, Netzwerken und anderen Nutzern. Ein robustes Skript zeichnet sich dadurch aus, dass es die Hälfte seines Codes für Fehlerbehandlung aufwendet. Wer nur den „Happy Path“ programmiert, wird früher oder später am Wochenende angerufen, weil ein System steht. Es braucht Zeit, diese defensive Denkweise zu entwickeln. Man muss Fehler provozieren – absichtlich Verzeichnisse sperren, volle Festplatten simulieren oder Netzwerkstecker ziehen –, um zu sehen, wie sich die eigene Logik verhält. Nur wer diese Schmerzen einmal im Testsystem gespürt hat, schreibt Code, der im produktiven Betrieb wirklich besteht. Es gibt keine Abkürzung zur Erfahrung; man muss bereit sein, seine Annahmen immer wieder zu hinterfragen und den Code so zu bauen, dass er im Zweifelsfall lieber sicher abbricht, als mit falschen Daten weiterzumachen. Wer das beherrscht, spart am Ende nicht nur Zeit, sondern bewahrt auch seine professionelle Integrität. Es ist nun mal so: Ein Skript ist nur so gut wie sein Verhalten im schlimmsten anzunehmenden Fall. Alles andere ist nur Glückssache, und Glück ist im Rechenzentrum eine schlechte Währung.

TS

Thomas Schäfer

Thomas Schäfer verfolgt politische und soziale Debatten mit kritischem Blick und journalistischer Verantwortung.