mitmario.dev

Pfade mit path

Node.js Sandbox 3 Min Lesezeit 3 BeispieleLektion 4 von 7

Ein Pfad ist am Ende nur eine Zeichenkette, und deshalb ist die Versuchung groß, ihn selbst zusammenzusetzen. ordner + "/" + datei sieht harmlos aus und funktioniert auf dem eigenen Rechner auch. Das Problem kommt später, an einem Ort, an dem du es nicht mehr mit dieser Zeile in Verbindung bringst.

Was beim Aneinanderkleben schiefgeht

Zusammensetzen statt aneinanderkleben
import path from "node:path";

const ordner = "daten/";
const datei = "/liste.json";

console.log("Selbst gebaut:", ordner + datei);
console.log("Mit join:", path.join(ordner, datei));
console.log("Eine Ebene hoch:", path.join("daten", "..", "bilder", "logo.png"));

Im Beispiel endet der Ordner auf einen Schrägstrich und die Datei fängt mit einem an. Zusammengeklebt ergibt das zwei Schrägstriche mitten im Pfad. Unter Linux geht das oft noch gut, in einer Fehlermeldung sieht es dann seltsam aus, und beim Vergleich zweier Pfade sind es plötzlich zwei verschiedene Zeichenketten.

path.join räumt das auf. Es setzt genau einen Trenner zwischen die Teile, wirft doppelte weg und löst .. gleich mit auf.

Dazu kommt der Punkt, wegen dem es node:path überhaupt gibt: Windows trennt Pfade mit einem Backslash, Linux und macOS mit einem Schrägstrich. Wenn du den Trenner selbst tippst, entscheidest du dich für ein Betriebssystem. Wenn du path.join benutzt, entscheidet Node, und zwar dort, wo das Programm gerade läuft. Der Server ist fast immer ein anderer als dein Arbeitsrechner.

Einen Pfad wieder auseinandernehmen

Der umgekehrte Weg kommt genauso oft vor: Aus einem vollständigen Pfad willst du nur einen Teil.

Einen Pfad in seine Teile zerlegen
import path from "node:path";

const pfad = "/vercel/sandbox/lern/daten/liste.json";

console.log("Ordner:", path.dirname(pfad));
console.log("Datei:", path.basename(pfad));
console.log("Name:", path.basename(pfad, ".json"));
console.log("Endung:", path.extname(pfad));

dirname gibt den Ordner, basename die Datei, extname die Endung. basename nimmt optional noch die Endung als zweites Argument entgegen und schneidet sie ab, was praktisch ist, wenn du aus liste.json den Namen liste brauchst.

Auch hier gilt: Das ließe sich mit split("/") und slice nachbauen. Aber die eingebaute Fassung kennt die Sonderfälle, an die du in dem Moment nicht denkst, und sie liest sich in einem halben Jahr noch von selbst.

Der Unterschied, an dem Programme umfallen

Jetzt der wichtigste Teil dieser Lektion. Es gibt zwei Orte, und sie sehen sich zum Verwechseln ähnlich.

process.cwd() ist das Arbeitsverzeichnis. Also der Ordner, in dem jemand stand, als er den Befehl abgeschickt hat. Er hat nichts damit zu tun, wo deine Datei liegt.

import.meta.dirname ist der Ordner deiner Datei. In CommonJS heißt derselbe Wert __dirname.

Solange du dein Programm aus seinem eigenen Ordner startest, sind beide gleich, und genau deshalb fällt der Unterschied auf dem eigenen Rechner monatelang nicht auf.

Zwei Orte, die gern verwechselt werden
import path from "node:path";

console.log("Arbeitsverzeichnis:", process.cwd());
console.log("Verzeichnis der Datei:", import.meta.dirname);
console.log("Falsch:", path.join(process.cwd(), "daten", "liste.json"));
console.log("Richtig:", path.join(import.meta.dirname, "daten", "liste.json"));

Hier liegt die Datei in app/, gestartet wurde aber eine Ebene darüber mit node app/index.mjs. Und schon zeigen die beiden Zeilen auf verschiedene Orte. Die Variante mit process.cwd() sucht die Daten neben dem Startort, findet dort nichts und meldet ENOENT, obwohl die Datei existiert.

Genau so entsteht der Klassiker: Auf deinem Rechner läuft alles, auf dem Server nicht. Dort startet nämlich ein Dienst dein Programm, und der steht in irgendeinem Verzeichnis, das mit deinem Projekt nichts zu tun hat.

Die Faustregel

Gehört die Datei zu deinem Programm, geh von import.meta.dirname aus. Vorlagen, Konfigurationsdateien, mitgelieferte Daten liegen neben dem Code und wandern mit ihm mit.

Gibt der Benutzer den Pfad an, ist process.cwd() richtig. Wer ein Kommandozeilenwerkzeug mit werkzeug bericht.txt aufruft, meint die Datei in seinem aktuellen Ordner, nicht eine neben dem Werkzeug. Abschnitt 4 baut genau so ein Werkzeug.

Für den Rest gibt es noch path.resolve. Es macht aus allem, was du hineingibst, einen absoluten Pfad, und zwar ausgehend vom Arbeitsverzeichnis. Praktisch, wenn du Benutzereingaben absichern willst, und Abschnitt 15 kommt darauf zurück.

Zum Mitnehmen

process.cwd() ist der Ort, an dem der Befehl abgesetzt wurde. import.meta.dirname ist der Ort, an dem deine Datei liegt. Das sind zwei verschiedene Dinge.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.