Aufwärmen · Aufgabe 1 von 7
Aufwärmen aus B3.2: docs ist ein Ordner, keine Datei. Was gibt dieses Programm aus?
import {readFile} from 'node:fs/promises';
try {
await readFile('docs', 'utf8');
} catch (error) {
console.log(error.code);
}// B3.4 · ca. 33 Min. · Einstieg
Nach dieser Lektion legen Sie Ordner an, listen sie auf und entfernen sie, lesen Größe, Typ und Zeit eines Eintrags mit stat und behandeln Ordnerfehler anhand ihres Codes.
Danach können Sie
Aufwärmen · Aufgabe 1 von 7
import {readFile} from 'node:fs/promises';
try {
await readFile('docs', 'utf8');
} catch (error) {
console.log(error.code);
}Vorhersagen · Aufgabe 2 von 7
import {readdir} from 'node:fs/promises';
const names = await readdir('docs');
console.log(names.sort().join(', '));Üben · Aufgabe 3 von 7
const entries = await readdir("docs", {____: true});
console.log(entries.filter((e) => e.isDirectory()).map((e) => e.name).sort().join(", "));Üben · Aufgabe 4 von 7
Üben · Aufgabe 5 von 7
import {stat, writeFile} from 'node:fs/promises';
const text = 'Grüße\n';
await writeFile('greeting.txt', text);
const info = await stat('greeting.txt');
console.log(text.length, info.size);Denksport · Aufgabe 6 von 7
import {stat, symlink, writeFile} from 'node:fs/promises';
await writeFile('big.txt', 'x'.repeat(1000));
await symlink('big.txt', 'link.txt');
const info = await stat('link.txt');
console.log(info.isSymbolicLink(), info.size);Anwenden · Aufgabe 7 von 7
Prüfen Sie Ihr Ergebnis anhand dieser Liste
Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.
Ausgearbeitetes Beispiel
Dieses Programm baut neben main.js einen kleinen Ordnerbaum namens demo, betrachtet ihn auf drei Arten und entfernt ihn wieder. mkdir legt drei Ebenen auf einmal an und meldet den ersten Ordner, den es angelegt hat. readdir mit withFileTypes und stat beschreiben die oberste Ebene, nach Namen sortiert; readdir mit recursive listet jeden Pfad darunter. rmdir lehnt den Ordner notes ab, weil er nicht leer ist, rm mit recursive entfernt ihn, und stat mit throwIfNoEntry: false bestätigt, dass er weg ist. Das Programm gibt nie eine Zeit aus, also ist seine Ausgabe bei jedem Lauf dieselbe.
main.js
import {mkdir, readdir, rm, rmdir, stat, writeFile} from 'node:fs/promises';
import {join, relative} from 'node:path';
const root = join(import.meta.dirname, 'demo');
await rm(root, {recursive: true, force: true}); // start clean, even if demo is missing
const first = await mkdir(join(root, 'notes', '2026'), {recursive: true});
console.log('first folder created:', relative(import.meta.dirname, first));
await writeFile(join(root, 'todo.txt'), 'buy milk\n');
await writeFile(join(root, 'README.md'), '# Demo\n');
await writeFile(join(root, 'notes', '2026', 'jan.md'), 'Grüße\n');
// The top level: Dirent for the type, stat for the size. Sorted by name.
const byName = (a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
for (const entry of (await readdir(root, {withFileTypes: true})).sort(byName)) {
if (entry.isDirectory()) console.log(` ${entry.name}/`);
else console.log(` ${entry.name} ${(await stat(join(root, entry.name))).size} bytes`);
}
// Every path below demo, sorted.
console.log('all:', (await readdir(root, {recursive: true})).sort().join(', '));
try {
await rmdir(join(root, 'notes'));
} catch (error) {
console.log('rmdir notes:', error.code);
}
await rm(join(root, 'notes'), {recursive: true});
console.log('notes after rm:', await stat(join(root, 'notes'), {throwIfNoEntry: false}));
await rm(root, {recursive: true});
Ausführen mit
node main.jsAusgabe
first folder created: demo
README.md 7 bytes
notes/
todo.txt 9 bytes
all: README.md, notes, notes/2026, notes/2026/jan.md, todo.txt
rmdir notes: ENOTEMPTY
notes after rm: undefinedÜbung 1 von 2
readdir und stat liefern Ihnen Datensätze wie {name: "img", size: 4096, isDirectory: true}. Schreiben Sie summarize(entries), das die Zeilen einer Liste zurückgibt: zuerst Ordner, als img/, dann Dateien, als notes.txt (120 bytes), jede Gruppe nach Namen sortiert, wie sort() Strings sortiert, und zuletzt eine Zeile wie 1 folders, 2 files, 150 bytes. Zählen Sie nur die Bytes der Dateien: Die Größe eines Ordners ist nicht sein Inhalt. Dieser Teil ist rein, er läuft also auch im Browser.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.
Teilen Sie die Liste mit filter in zwei: entries.filter((e) => e.isDirectory) und den Rest.
Sortieren Sie jeden Teil mit einer Vergleichsfunktion: (a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0).
Addieren Sie nur die Größen der Dateien, mit reduce((sum, f) => sum + f.size, 0).
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
// entries: [{name, size, isDirectory}], as readdir and stat describe them.
// Folders first, then files, each sorted by name; a summary line last.
export function summarize(entries) {
const byName = (a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
const folders = entries.filter((e) => e.isDirectory).sort(byName);
const files = entries.filter((e) => !e.isDirectory).sort(byName);
const bytes = files.reduce((sum, f) => sum + f.size, 0);
return [
...folders.map((f) => f.name + '/'),
...files.map((f) => f.name + ' (' + f.size + ' bytes)'),
folders.length + ' folders, ' + files.length + ' files, ' + bytes + ' bytes'
];
}
const entries = [
{name: 'notes.txt', size: 120, isDirectory: false},
{name: 'img', size: 4096, isDirectory: true},
{name: 'README.md', size: 30, isDirectory: false}
];
console.log(summarize(entries).join('\n'));
Installieren Sie Node.js 24 LTS oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
// entries: [{name, size, isDirectory}], as readdir and stat describe them.
// Folders first, then files, each sorted by name; a summary line last.
export function summarize(entries) {
return entries.map((e) => e.name);
}
const entries = [
{name: 'notes.txt', size: 120, isDirectory: false},
{name: 'img', size: 4096, isDirectory: true},
{name: 'README.md', size: 30, isDirectory: false}
];
console.log(summarize(entries).join('\n'));
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {summarize} from './main.js';
const entries = [
{name: 'notes.txt', size: 120, isDirectory: false},
{name: 'img', size: 4096, isDirectory: true},
{name: 'README.md', size: 30, isDirectory: false}
];
test('zuerst Ordner, dann Dateien, jeweils nach Namen sortiert', () => {
const got = summarize(entries).slice(0, 3);
assert.deepEqual(got, ['img/', 'README.md (30 bytes)', 'notes.txt (120 bytes)'], `die ersten Zeilen waren ${JSON.stringify(got)}`);
});
test('die letzte Zeile zählt die Ordner, die Dateien und deren Bytes', () => {
const got = summarize(entries).at(-1);
assert.equal(got, '1 folders, 2 files, 150 bytes', `die letzte Zeile war ${JSON.stringify(got)}`);
});
test('auch zwei Ordner werden sortiert', () => {
const got = summarize([{name: 'src', size: 4096, isDirectory: true}, {name: 'docs', size: 4096, isDirectory: true}]);
assert.deepEqual(got, ['docs/', 'src/', '2 folders, 0 files, 0 bytes'], `summarize lieferte ${JSON.stringify(got)}`);
});
test('ein leerer Ordner ergibt nur die Zusammenfassungszeile', () => {
const got = summarize([]);
assert.deepEqual(got, ['0 folders, 0 files, 0 bytes'], `summarize([]) lieferte ${JSON.stringify(got)}`);
});
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenÜbung 2 von 2
Schreiben Sie describe(dir), das den Ordner dir liest und pro Eintrag einen Datensatz {name, size, isDirectory} liefert, nach Namen sortiert. Nehmen Sie readdir mit withFileTypes für den Typ und stat mit join(dir, entry.name) für die Größe. Ein fehlender Ordner (ENOENT) ergibt eine leere Liste; jeder andere Fehler, etwa ENOTDIR für eine Datei, muss ablehnen. Der Code darunter baut einen Beispielordner und gibt ihn aus. Starten Sie node main.js und prüfen Sie mit node --test.
Diese Übung braucht Node.js auf Ihrem Computer (die Browser-Version kann sie nicht ausführen). Dateien und Befehle stehen unten.
readdir(dir, {withFileTypes: true}) liefert Dirent-Objekte: entry.name und entry.isDirectory().
Geben Sie im catch-Block nur dann [] zurück, wenn error.code ENOENT ist, und werfen Sie den Fehler sonst erneut.
stat(join(dir, entry.name)).size ist die Größe; sortieren Sie die Datensätze mit einer Vergleichsfunktion über name.
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
import {mkdir, readdir, rm, stat, writeFile} from 'node:fs/promises';
import {join} from 'node:path';
// Describes the entries of dir as {name, size, isDirectory}, sorted by name.
// A missing dir gives an empty list; every other error is thrown again.
export async function describe(dir) {
let entries;
try {
entries = await readdir(dir, {withFileTypes: true});
} catch (error) {
if (error.code === 'ENOENT') return [];
throw error;
}
const records = [];
for (const entry of entries) {
const info = await stat(join(dir, entry.name));
records.push({name: entry.name, size: info.size, isDirectory: entry.isDirectory()});
}
return records.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
}
// A sample folder next to main.js, made afresh on every run.
const sample = join(import.meta.dirname, 'sample');
await rm(sample, {recursive: true, force: true});
await mkdir(join(sample, 'img'), {recursive: true});
await writeFile(join(sample, 'todo.txt'), 'buy milk\n');
await writeFile(join(sample, 'README.md'), '# Sample\n');
for (const r of await describe(sample)) console.log(r.isDirectory ? r.name + '/' : r.name + ' ' + r.size + ' bytes');
console.log('missing:', await describe(join(import.meta.dirname, 'missing')));
Installieren Sie Node.js 24 LTS oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
import {mkdir, readdir, rm, stat, writeFile} from 'node:fs/promises';
import {join} from 'node:path';
// Describes the entries of dir as {name, size, isDirectory}, sorted by name.
// A missing dir gives an empty list; every other error is thrown again.
export async function describe(dir) {
let names;
try {
names = await readdir(dir);
} catch {
return [];
}
return names.map((name) => ({name, size: 0, isDirectory: false}));
}
// A sample folder next to main.js, made afresh on every run.
const sample = join(import.meta.dirname, 'sample');
await rm(sample, {recursive: true, force: true});
await mkdir(join(sample, 'img'), {recursive: true});
await writeFile(join(sample, 'todo.txt'), 'buy milk\n');
await writeFile(join(sample, 'README.md'), '# Sample\n');
for (const r of await describe(sample)) console.log(r.isDirectory ? r.name + '/' : r.name + ' ' + r.size + ' bytes');
console.log('missing:', await describe(join(import.meta.dirname, 'missing')));
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {runMain} from './learnrun.js';
import {describe} from './main.js';
test('gibt den Beispielordner nach Namen sortiert aus, mit den Dateigrößen', async () => {
const got = (await runMain()).trimEnd().split('\n').slice(0, 3);
assert.deepEqual(got, ['README.md 9 bytes', 'img/', 'todo.txt 9 bytes'], `das Programm gab ${JSON.stringify(got)} aus`);
});
test('ein fehlender Ordner ergibt eine leere Liste', async () => {
const last = (await runMain()).trimEnd().split('\n').at(-1);
assert.equal(last, 'missing: []', `die letzte Zeile war ${JSON.stringify(last)}`);
});
test('eine Datei statt eines Ordners lehnt mit ENOTDIR ab', async () => {
await assert.rejects(describe(new URL('./main.js', import.meta.url)), {code: 'ENOTDIR'}, 'describe auf main.js sollte mit dem Code ENOTDIR ablehnen');
});
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenimport {readdir} from 'node:fs/promises';
for (const entry of await readdir('.')) {
if (entry.isDirectory()) console.log(entry + '/');
}
Was Node.js ausgibt
TypeError: entry.isDirectory is not a functionWarum, und die Lösung
Ohne Optionen liefert readdir Strings, und ein String hat kein isDirectory. Fordern Sie Dirent-Objekte an: readdir(".", {withFileTypes: true}), und nehmen Sie dann entry.name für den Namen und entry.isDirectory() für den Typ.
import {mkdir, rmdir, writeFile} from 'node:fs/promises';
await mkdir('data', {recursive: true});
await writeFile('data/notes.json', '[]\n');
await rmdir('data');
Was Node.js ausgibt
Error: ENOTEMPTY: directory not empty, rmdir 'data'Warum, und die Lösung
rmdir entfernt nur einen leeren Ordner. Um einen Ordner mit allem darin zu entfernen, nehmen Sie rm("data", {recursive: true}); ergänzen Sie force: true, wenn auch ein fehlender Ordner kein Fehler sein soll. Prüfen Sie, dass der Pfad der gemeinte ist: Rekursives Entfernen fragt nicht nach.
import {mkdir, rm, writeFile} from 'node:fs/promises';
await mkdir('data', {recursive: true});
await writeFile('data/notes.json', '[]\n');
await rm('data');
Was Node.js ausgibt
SystemError [ERR_FS_EISDIR]: Path is a directory: rm returned EISDIR (is a directory) dataWarum, und die Lösung
Ohne Optionen entfernt rm nur Dateien, und ein Ordner wird abgelehnt. Beachten Sie den Code: error.code ist hier ERR_FS_EISDIR, nicht EISDIR. Übergeben Sie {recursive: true}, um den Ordner mit Inhalt zu entfernen.
JavaScript im Browser: die eigene Engine Ihres Browsers in einem abgeschotteten Worker. Syntaxfehler findet acorn 8.18.0, MIT. Lizenz und Quellcode
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.