path.join() verbindet Pfadsegmente mit dem richtigen Trennzeichen und räumt .. und doppelte Schrägstriche auf, lässt das Ergebnis aber relativ, wenn du mit einem relativen Segment angefangen hast. path.resolve() macht dieselbe Arbeit von rechts nach links und gibt immer einen absoluten Pfad zurück: Es startet beim aktuellen Arbeitsverzeichnis, und jedes absolute Segment unterwegs beginnt den Pfad neu. Nimm join, um einen Pfad zu bauen, und resolve, wenn du einen absoluten brauchst.
Es hat users/ada/notes.txt ausgegeben, dann users/grace/todo.txt (das .. hat ada entfernt), dann /srv/app/public/index.html und src/lib/utils.js: Überzählige Schrägstriche und leere Segmente sind weg. join behandelt /public/ nie als Neuanfang; es ist einfach das nächste Segment.
path.join vs. path.resolve
Der Unterschied zeigt sich, wenn ein Segment mit / beginnt oder wenn das erste Segment relativ ist:
Mit /public in der Mitte hat join /srv/app/public/img erzeugt, während resolve alles davor verworfen und /public/img erzeugt hat; .. haben beide gleich behandelt. Die letzten beiden Zeilen zeigen, dass join public/index.html relativ gelassen hat, während resolve es absolut gemacht hat, indem es den aktuellen Ordner davorgesetzt hat.
path.join | path.resolve | |
|---|---|---|
| Ergebnis | Relativ oder absolut, wie die Eingabe | Immer absolut |
Ein Segment, das mit / beginnt | Nur ein weiteres Segment | Beginnt den Pfad dort neu |
| Startpunkt | Keiner | Das aktuelle Arbeitsverzeichnis |
| Ohne Argumente | '.' | Das aktuelle Arbeitsverzeichnis |
| Typischer Einsatz | Einen Pfad aus Teilen bauen | Nutzereingaben oder Konfiguration in einen absoluten Pfad verwandeln |
basename, dirname, extname und parse
Diese Funktionen nehmen einen Pfad auseinander. Sie schauen nur auf den String: Die Datei muss nicht existieren.
Es hat cat.final.jpg, cat.final, /home/ada/photos und .jpg ausgegeben: extname nimmt nur den Teil nach dem letzten Punkt. path.parse gibt alles auf einmal zurück (root, dir, base, ext, name), und path.format macht aus so einem Objekt wieder einen String.
__dirname und __filename
In einer CommonJS-Datei ist __dirname der absolute Pfad des Ordners, der die Datei enthält, und __filename die Datei selbst. Ein relativer Pfad wie 'data.json' ist relativ dazu, wo der Nutzer node gestartet hat, und das bricht, sobald jemand dein Skript aus einem anderen Ordner startet. Bau Pfade stattdessen aus __dirname:
Es hat config.json dark und index.js ausgegeben. Die vollständigen Pfade sind auf jedem Rechner anders, genau deshalb baust du sie mit path.join(__dirname, ...), statt sie abzutippen.
__dirname in ES-Modulen: import.meta.dirname
ES-Module haben kein __dirname; wer es benutzt, bekommt ReferenceError: __dirname is not defined in ES module scope. Seit Node.js 20.11 ersetzen import.meta.dirname und import.meta.filename sie:
Es hat index.js und data/users.json ausgegeben. In Node.js-Versionen vor 20.11 leitest du den Ordner aus der Modul-URL ab: path.dirname(fileURLToPath(import.meta.url)), mit fileURLToPath aus node:url.
Warum du Pfade nicht mit Strings zusammensetzt
'data' + '/' + file funktioniert unter macOS und Linux und scheitert dann unter Windows, wo das Trennzeichen \ ist, und es tut nichts gegen .., doppelte oder fehlende Schrägstriche. path nutzt die Regeln des Systems, auf dem es läuft. Das Windows-Ergebnis siehst du auf jedem Rechner mit path.win32:
const path = require('path');
path.posix.join('users', 'ada', 'notes.txt'); // 'users/ada/notes.txt'
path.win32.join('users', 'ada', 'notes.txt'); // 'users\\ada\\notes.txt'
path.sep; // '/' on macOS and Linux, '\\' on Windows
Übergib das Ergebnis von path.join an fs.readFile, und derselbe Code liest die Datei auf jedem System.
Häufige Fehler
Erwarten, dass extname doppelte Endungen kennt. path.extname('archive.tar.gz') ist '.gz', und eine Dotfile wie .env hat gar keine Endung: path.extname('.env') ist ''. Prüf das Ende des Namens selbst, wenn die ganze Endung wichtig ist.
Sich darauf verlassen, dass join einen Nutzer in einem Ordner hält. path.join('/srv/app', '../../etc/passwd') ist '/etc/passwd'. Bevor du eine Datei liest, die eine Anfrage benennt, löse den Pfad auf und prüf, dass er noch mit deinem Ordner beginnt.
Häufig gestellte Fragen
Was ist der Unterschied zwischen __dirname und process.cwd()?
__dirname ist der Ordner der aktuellen Quelldatei und ändert sich nie. process.cwd() ist der Ordner, aus dem der Nutzer node gestartet hat. Gleich sind sie nur, wenn das Programm aus seinem eigenen Ordner gestartet wird.
Soll ich mit __dirname path.join oder path.resolve nehmen?
Für path.join(__dirname, 'data.json') geht beides, weil __dirname schon absolut ist. Nimm resolve, wenn ein Segment selbst absolut sein könnte, etwa ein vom Nutzer eingegebener Pfad, und dieser Pfad gewinnen soll.
Wie nutze ich das path-Modul mit URLs?
Gar nicht: URLs nutzen immer / und haben eigene Regeln für .. und Query-Strings. Nimm die Klasse URL, wie in new URL('../img/logo.png', 'https://example.com/docs/page').