Python: Wie legt man ein Verzeichnis samt fehlender Elternverzeichnisse an?

Ein Verzeichnis anzulegen ist ein Aufruf. Eines mehrere Ebenen tief anzulegen, ohne zu scheitern wenn es schon da ist und ohne Race Condition, braucht zwei Schlüsselwortargumente die man leicht vergisst.

Die Antwort

from pathlib import Path

Path("/tmp/a/b/c").mkdir(parents=True, exist_ok=True)
exists : True
is_dir : True

Beide Schlüsselwörter verdienen ihren Platz. Ohne parents=True ist ein fehlendes Zwischenverzeichnis ein Fehler:

mkdir() with a missing parent -> FileNotFoundError: [Errno 2] No such file or directory: '/tmp/demo0049/x/y'

Und ohne exist_ok=True ist ein bereits vorhandenes Verzeichnis ein Fehler:

mkdir(parents=True) on an existing dir -> FileExistsError: [Errno 17] File exists: '/tmp/demo0049/a/b/c'

Das Gegenstück aus os ist os.makedirs(pfad, exist_ok=True), wobei makedirs das parents=True schon impliziert. Beides ist in Ordnung; pathlib liest sich besser, sobald du im selben Code auch Pfade zusammensetzt und Dateien liest.

Warum man nicht vorher prüft

Die Variante, die man ohne nachzudenken schreibt:

if not pfad.exists():
    pfad.mkdir()

Zwischen dem exists() und dem mkdir() kann ein anderer Prozess das Verzeichnis anlegen — und dann wirft dein mkdir() genau den Fehler, den du vermeiden wolltest. Das ist ein Check-then-act-Rennen, und es geht selten genug schief, um rätselhaft zu sein.

exist_ok=True schiebt die ganze Sache in einen einzigen Systemaufruf, in dem der Kernel das entscheidet. Dasselbe Prinzip wie try/except statt if exists: die Sache tun und den Fehlschlag behandeln, statt eine Frage zu stellen, deren Antwort sofort veraltet.

exist_ok=True heißt nicht “falls dort irgendetwas ist”

Das sollte man wissen, denn der Name legt etwas anderes nahe. Leg eine Datei an diesen Pfad:

a FILE at the path, mkdir(exist_ok=True) -> FileExistsError: [Errno 17] File exists: '/tmp/demo0049/clash'

exist_ok=True duldet ein vorhandenes Verzeichnis. Alles andere an diesem Pfad bleibt ein Fehler — was richtig so ist, und was man wissen sollte, wenn man versucht ist, es als “stell sicher dass dieser Pfad benutzbar ist” zu lesen.

Rechte, und die beiden Überraschungen

mkdir() nimmt ein mode entgegen, und der Wert den du übergibst ist nicht der Wert den du bekommst:

mkdir(mode=0o700) -> drwx------
mkdir(mode=0o777) -> drwxr-xr-x
current umask     : 0o22

Der Modus wird mit der umask des Prozesses maskiert, 0o777 ergibt bei der üblichen umask 0o022 also 0o755. Das ist normales Unix-Verhalten und keine Python-Eigenart, erwischt aber jeden, der einen Modus setzt und danach nachsieht.

Die zweite Überraschung ist spezifischer:

with parents=True, the PARENT gets the default mode, not yours:
  parent drwxr-xr-x  inner drwx------

In einem Aufruf mit mode=0o700, parents=True angelegt — den Modus hat nur das unterste Verzeichnis bekommen. Die Elternverzeichnisse entstanden mit der Voreinstellung. Wer einen Baum aufbaut, der privat sein soll, hat die Zwischenverzeichnisse also offen, und gewarnt wird man nicht.

Setz die Rechte anschließend explizit, wenn es darauf ankommt, mit Path.chmod.

Der Fall, den man tatsächlich hat

In neun von zehn Fällen willst du gleich eine Datei schreiben, und ihr Verzeichnis gibt es womöglich noch nicht:

zieldatei = Path("berichte") / "2026" / "aus.txt"
zieldatei.parent.mkdir(parents=True, exist_ok=True)
zieldatei.write_text("fertig\n")

.parent ist der ganze Kniff — du hast den Dateipfad und brauchst das Verzeichnis darum herum.

Wenn es wieder weg soll

Ist das Verzeichnis temporär, überlass die Arbeit Python:

with tempfile.TemporaryDirectory() as tmp:
    ...
inside the with block : True
after the with block  : False

tempfile.TemporaryDirectory räumt den Baum beim Verlassen weg, auch bei einer Exception. Deutlich besser als ein selbst gebauter Pfad in /tmp, der jeden Absturz überlebt.

Beim Löschen von Hand gibt es eine Asymmetrie, die man sich merken sollte:

Path.rmdir() only removes an EMPTY directory:
  -> OSError: [Errno 39] Directory not empty

rmdir() verweigert ein nicht-leeres Verzeichnis. shutil.rmtree entfernt den ganzen Baum ohne nachzufragen — was genau so gefährlich ist, wie es klingt.

Hinweis zu Netcup (Werbung)

Der deutsche Hoster Netcup bietet unter anderem günstige und zugleich leistungsstarke Webhosting Pakete, KVM-basierte Root Server und dezidierte Server an. Mit unseren Gutscheincodes kannst du noch mehr Geld sparen (6€ bei deiner ersten Bestellung, 30% Rabatt auf alle KVM-basierten Root Server, ...).