Dieses Kapitel enthält
Da der Fokus dieses Buches auf Shell-Skripting liegt, arbeiten wir nur mit Textdaten.
fs.open() usw.)fs.openSync(path, flags?, mode?) öffnet einen neuen Dateideskriptor für eine Datei unter einem bestimmten Pfad und gibt ihn zurück.fs.closeSync(fd) schließt einen Dateideskriptor.fs.fchmodSync(fd, mode)fs.fchownSync(fd, uid, gid)fs.fdatasyncSync(fd)fs.fstatSync(fd, options?)fs.fsyncSync(fd)fs.ftruncateSync(fd, len?)fs.futimesSync(fd, atime, mtime)FileHandle, die auf Dateideskriptoren basiert. Instanzen werden über fsPromises.open() erstellt. Verschiedene Operationen werden über Methoden bereitgestellt (nicht über Funktionen)fileHandle.close()fileHandle.chmod(mode)fileHandle.chown(uid, gid)Beachten Sie, dass wir in diesem Kapitel (3) nicht verwenden – (1) und (2) reichen für unsere Zwecke aus.
Funktionen, deren Namen mit einem „l“ beginnen, arbeiten normalerweise mit symbolischen Links
fs.lchmodSync(), fs.lchmod(), fsPromises.lchmod()fs.lchownSync(), fs.lchown(), fsPromises.lchown()fs.lutimesSync(), fs.lutimes(), fsPromises.lutimes()Funktionen, deren Namen mit einem „f“ beginnen, verwalten normalerweise Dateideskriptoren
fs.fchmodSync(), fs.fchmod()fs.fchownSync(), fs.fchown()fs.fstatSync(), fs.fstat()Mehrere Klassen spielen in den Dateisystem-APIs von Node eine wichtige Rolle.
Immer wenn eine Node.js-Funktion einen Dateisystempfad als String akzeptiert (Zeile A), akzeptiert sie normalerweise auch eine Instanz von URL (Zeile B)
assert.equal(
fs.readFileSync(
'/tmp/text-file.txt', {encoding: 'utf-8'}), // (A)
'Text content'
);
assert.equal(
fs.readFileSync(
new URL('file:///tmp/text-file.txt'), {encoding: 'utf-8'}), // (B)
'Text content'
);Die manuelle Konvertierung zwischen Pfaden und file:-URLs scheint einfach zu sein, hat aber überraschend viele Fallstricke: Prozentkodierung oder -dekodierung, Windows-Laufwerksbuchstaben usw. Stattdessen ist es besser, die folgenden beiden Funktionen zu verwenden
Wir verwenden in diesem Kapitel keine Datei-URLs. Anwendungsfälle dafür werden in §7.11.1 „Klasse URL“ beschrieben.
Die Klasse Buffer repräsentiert Bytefolgen fester Länge in Node.js. Sie ist eine Unterklasse von Uint8Array (einem TypedArray). Buffer werden hauptsächlich bei der Arbeit mit Binärdateien verwendet und sind daher für dieses Buch von geringerem Interesse.
Immer wenn Node.js einen Buffer akzeptiert, akzeptiert es auch ein Uint8Array. Da Uint8Arrays plattformübergreifend sind und Buffer nicht, ist ersteres vorzuziehen.
Buffer können eine Sache, die Uint8Arrays nicht können: Text in verschiedenen Kodierungen kodieren und dekodieren. Wenn wir UTF-8 in Uint8Arrays kodieren oder dekodieren müssen, können wir die Klasse TextEncoder oder die Klasse TextDecoder verwenden. Diese Klassen sind auf den meisten JavaScript-Plattformen verfügbar
> new TextEncoder().encode('café')
Uint8Array.of(99, 97, 102, 195, 169)
> new TextDecoder().decode(Uint8Array.of(99, 97, 102, 195, 169))
'café'Einige Funktionen akzeptieren oder geben native Node.js-Streams zurück
stream.Readable ist die Klasse von Node für lesbare Streams. Das Modul node:fs verwendet fs.ReadStream, was eine Unterklasse ist.stream.Writable ist die Klasse von Node für schreibbare Streams. Das Modul node:fs verwendet fs.WriteStream, was eine Unterklasse ist.Anstelle von nativen Streams können wir nun plattformübergreifende Web-Streams auf Node.js verwenden. Wie das geht, wird in §10 „Web-Streams unter Node.js verwenden“ erklärt.
fs.readFileSync(filePath, options?) liest die Datei unter filePath in einen einzigen String
assert.equal(
fs.readFileSync('text-file.txt', {encoding: 'utf-8'}),
'there\r\nare\nmultiple\nlines'
);Vor- und Nachteile dieses Ansatzes (ggü. der Verwendung eines Streams)
Als Nächstes werden wir den eingelesenen String in Zeilen aufteilen.
Der folgende Code teilt einen String in Zeilen auf und entfernt dabei die Zeilenumbrüche. Er funktioniert sowohl mit Unix- als auch mit Windows-Zeilenumbrüchen
const RE_SPLIT_EOL = /\r?\n/;
function splitLines(str) {
return str.split(RE_SPLIT_EOL);
}
assert.deepEqual(
splitLines('there\r\nare\nmultiple\nlines'),
['there', 'are', 'multiple', 'lines']
);„EOL“ steht für „End Of Line“. Wir akzeptieren sowohl Unix-Zeilenumbrüche ('\n') als auch Windows-Zeilenumbrüche ('\r\n', wie der erste im vorherigen Beispiel). Weitere Informationen finden Sie in §8.3 „Umgang mit Zeilenumbrüchen über Plattformen hinweg“.
Der folgende Code teilt einen String in Zeilen auf und behält dabei die Zeilenumbrüche bei. Er funktioniert sowohl mit Unix- als auch mit Windows-Zeilenumbrüchen („EOL“ steht für „End Of Line“)
const RE_SPLIT_AFTER_EOL = /(?<=\r?\n)/; // (A)
function splitLinesWithEols(str) {
return str.split(RE_SPLIT_AFTER_EOL);
}
assert.deepEqual(
splitLinesWithEols('there\r\nare\nmultiple\nlines'),
['there\r\n', 'are\n', 'multiple\n', 'lines']
);
assert.deepEqual(
splitLinesWithEols('first\n\nthird'),
['first\n', '\n', 'third']
);
assert.deepEqual(
splitLinesWithEols('EOL at the end\n'),
['EOL at the end\n']
);
assert.deepEqual(
splitLinesWithEols(''),
['']
);Zeile A enthält einen regulären Ausdruck mit einer Lookbehind-Assertion. Er passt an Stellen, denen ein Treffer des Musters \r?\n vorausgeht, aber er erfasst nichts. Daher entfernt er nichts zwischen den String-Fragmenten, in die der Eingabestring aufgeteilt wird.
Auf Engines, die Lookbehind-Assertions nicht unterstützen (siehe diese Tabelle), können wir die folgende Lösung verwenden
function splitLinesWithEols(str) {
if (str.length === 0) return [''];
const lines = [];
let prevEnd = 0;
while (prevEnd < str.length) {
// Searching for '\n' means we’ll also find '\r\n'
const newlineIndex = str.indexOf('\n', prevEnd);
// If there is a newline, it’s included in the line
const end = newlineIndex < 0 ? str.length : newlineIndex+1;
lines.push(str.slice(prevEnd, end));
prevEnd = end;
}
return lines;
}Diese Lösung ist einfach, aber umständlicher.
In beiden Versionen von splitLinesWithEols() akzeptieren wir wieder sowohl Unix-Zeilenumbrüche ('\n') als auch Windows-Zeilenumbrüche ('\r\n'). Weitere Informationen finden Sie in §8.3 „Umgang mit Zeilenumbrüchen über Plattformen hinweg“.
Wir können Textdateien auch über Streams lesen
import {Readable} from 'node:stream';
const nodeReadable = fs.createReadStream(
'text-file.txt', {encoding: 'utf-8'});
const webReadableStream = Readable.toWeb(nodeReadable);
const lineStream = webReadableStream.pipeThrough(
new ChunksToLinesStream());
for await (const line of lineStream) {
console.log(line);
}
// Output:
// 'there\r\n'
// 'are\n'
// 'multiple\n'
// 'lines'Wir haben die folgende externe Funktionalität verwendet
fs.createReadStream(filePath, options?) erstellt einen Node.js-Stream (eine Instanz von stream.Readable).stream.Readable.toWeb(streamReadable) konvertiert einen lesbaren Node.js-Stream in einen Web-Stream (eine Instanz von ReadableStream).ChunksToLinesStream wird in §10.7.1 „Beispiel: Umwandlung eines Streams aus beliebigen Chunks in einen Stream aus Zeilen“ erklärt. Chunks sind die Datenstücke, die von Streams erzeugt werden. Wenn wir einen Stream haben, dessen Chunks Strings beliebiger Länge sind, und ihn durch einen ChunksToLinesStream leiten, erhalten wir einen Stream, dessen Chunks Zeilen sind.Web-Streams sind asynchron iterierbar, weshalb wir eine for-await-of-Schleife verwenden können, um über die Zeilen zu iterieren.
Wenn uns keine Textzeilen interessieren, benötigen wir ChunksToLinesStream nicht, können über webReadableStream iterieren und erhalten Chunks mit beliebiger Länge.
Weitere Informationen
Web-Streams werden in §10 „Web-Streams unter Node.js verwenden“ behandelt.
Zeilenumbrüche werden in §8.3 „Umgang mit Zeilenumbrüchen über Plattformen hinweg“ behandelt.
Vor- und Nachteile dieses Ansatzes (ggü. dem Lesen eines einzelnen Strings)
fs.writeFileSync(filePath, str, options?) schreibt str in eine Datei unter filePath. Wenn an diesem Pfad bereits eine Datei existiert, wird sie überschrieben.
Der folgende Code zeigt, wie diese Funktion verwendet wird
fs.writeFileSync(
'new-file.txt',
'First line\nSecond line\n',
{encoding: 'utf-8'}
);Informationen zu Zeilenumbrüchen finden Sie in §8.3 „Umgang mit Zeilenumbrüchen über Plattformen hinweg“.
Vor- und Nachteile (ggü. der Verwendung eines Streams)
Der folgende Code hängt eine Textzeile an eine vorhandene Datei an
fs.appendFileSync(
'existing-file.txt',
'Appended line\n',
{encoding: 'utf-8'}
);Wir können auch fs.writeFileSync() für diese Aufgabe verwenden
fs.writeFileSync(
'existing-file.txt',
'Appended line\n',
{encoding: 'utf-8', flag: 'a'}
);Dieser Code ist fast identisch mit dem, den wir zum Überschreiben vorhandener Inhalte verwendet haben (siehe vorheriger Abschnitt für weitere Informationen). Der einzige Unterschied besteht darin, dass wir die Option .flag hinzugefügt haben: Der Wert 'a' bedeutet, dass wir Daten anhängen. Andere mögliche Werte (z. B. zum Auslösen eines Fehlers, wenn eine Datei noch nicht existiert) werden in der Node.js-Dokumentation erklärt.
Achtung: In einigen Funktionen heißt diese Option .flag, in anderen .flags.
Der folgende Code verwendet einen Stream, um mehrere Strings in eine Datei zu schreiben
import {Writable} from 'node:stream';
const nodeWritable = fs.createWriteStream(
'new-file.txt', {encoding: 'utf-8'});
const webWritableStream = Writable.toWeb(nodeWritable);
const writer = webWritableStream.getWriter();
try {
await writer.write('First line\n');
await writer.write('Second line\n');
await writer.close();
} finally {
writer.releaseLock()
}Wir haben die folgenden Funktionen verwendet
fs.createWriteStream(path, options?) erstellt einen Node.js-Stream (eine Instanz von stream.Writable).stream.Writable.toWeb(streamWritable) konvertiert einen beschreibbaren Node.js-Stream in einen Web-Stream (eine Instanz von WritableStream).Weitere Informationen
Vor- und Nachteile (ggü. dem Schreiben eines einzelnen Strings)
Der folgende Code verwendet einen Stream, um Text an eine vorhandene Datei anzuhängen
import {Writable} from 'node:stream';
const nodeWritable = fs.createWriteStream(
'existing-file.txt', {encoding: 'utf-8', flags: 'a'});
const webWritableStream = Writable.toWeb(nodeWritable);
const writer = webWritableStream.getWriter();
try {
await writer.write('First appended line\n');
await writer.write('Second appended line\n');
await writer.close();
} finally {
writer.releaseLock()
}Dieser Code ist fast identisch mit dem, den wir zum Überschreiben vorhandener Inhalte verwendet haben (siehe vorheriger Abschnitt für weitere Informationen). Der einzige Unterschied besteht darin, dass wir die Option .flags hinzugefügt haben: Der Wert 'a' bedeutet, dass wir Daten anhängen. Andere mögliche Werte (z. B. zum Auslösen eines Fehlers, wenn eine Datei noch nicht existiert) werden in der Node.js-Dokumentation erklärt.
Achtung: In einigen Funktionen heißt diese Option .flag, in anderen .flags.
Leider haben nicht alle Plattformen die gleichen Zeilenumbruch-Zeichen, die das Zeilenende (EOL) markieren
'\r\n'.'\n'.Um EOL plattformübergreifend zu handhaben, können wir mehrere Strategien anwenden.
Beim Lesen von Text ist es am besten, beide EOLs zu erkennen.
Wie könnte das beim Aufteilen eines Textes in Zeilen aussehen? Wir können die EOLs (in beiden Formaten) am Ende einfügen. Dies ermöglicht es uns, so wenig wie möglich zu ändern, wenn wir diese Zeilen ändern und in eine Datei schreiben.
Bei der Verarbeitung von Zeilen mit EOLs ist es manchmal nützlich, sie zu entfernen – z. B. über die folgende Funktion
const RE_EOL_REMOVE = /\r?\n$/;
function removeEol(line) {
const match = RE_EOL_REMOVE.exec(line);
if (!match) return line;
return line.slice(0, match.index);
}
assert.equal(
removeEol('Windows EOL\r\n'),
'Windows EOL'
);
assert.equal(
removeEol('Unix EOL\n'),
'Unix EOL'
);
assert.equal(
removeEol('No EOL'),
'No EOL'
);Beim Schreiben von Zeilenumbrüchen haben wir zwei Optionen
EOL im Modul 'node:os' (hier) enthält den EOL der aktuellen Plattform.Die folgende Funktion durchläuft ein Verzeichnis und listet alle seine Nachkommen auf (seine Kinder, die Kinder seiner Kinder usw.)
import * as path from 'node:path';
function* traverseDirectory(dirPath) {
const dirEntries = fs.readdirSync(dirPath, {withFileTypes: true});
// Sort the entries to keep things more deterministic
dirEntries.sort(
(a, b) => a.name.localeCompare(b.name, 'en')
);
for (const dirEntry of dirEntries) {
const fileName = dirEntry.name;
const pathName = path.join(dirPath, fileName);
yield pathName;
if (dirEntry.isDirectory()) {
yield* traverseDirectory(pathName);
}
}
}Wir haben diese Funktionalität verwendet
fs.readdirSync(thePath, options?) gibt die Kinder des Verzeichnisses unter thePath zurück..withFileTypes true ist, gibt die Funktion Verzeichniseinträge zurück, Instanzen von fs.Dirent. Diese haben Eigenschaften wiedirent.namedirent.isDirectory()dirent.isFile()dirent.isSymbolicLink().withFileTypes false oder fehlt, gibt die Funktion Strings mit Dateinamen zurück.Der folgende Code zeigt traverseDirectory() in Aktion
for (const filePath of traverseDirectory('dir')) {
console.log(filePath);
}
// Output:
// 'dir/dir-file.txt'
// 'dir/subdir'
// 'dir/subdir/subdir-file1.txt'
// 'dir/subdir/subdir-file2.csv'mkdir, mkdir -p)Wir können die folgende Funktion zum Erstellen von Verzeichnissen verwenden
fs.mkdirSync(thePath, options?): undefined | stringoptions.recursive bestimmt, wie die Funktion das Verzeichnis unter thePath erstellt
.recursive fehlt oder false ist, gibt mkdirSync() undefined zurück und eine Ausnahme wird ausgelöst, wennthePath existiert.thePath existiert nicht..recursive true istthePath existiert.thePath werden bei Bedarf erstellt.mkdirSync() gibt den Pfad des ersten neu erstellten Verzeichnisses zurück.Dies ist mkdirSync() in Aktion
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
]
);
fs.mkdirSync('dir/sub/subsub', {recursive: true});
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
'dir/sub',
'dir/sub/subsub',
]
);Die Funktion traverseDirectory(dirPath) listet alle Nachkommen des Verzeichnisses unter dirPath auf.
Wenn wir bei Bedarf eine verschachtelte Dateistruktur einrichten möchten, können wir uns nicht immer sicher sein, dass die Elternverzeichnisse existieren, wenn wir eine neue Datei erstellen. Dann hilft die folgende Funktion
import * as path from 'node:path';
function ensureParentDirectory(filePath) {
const parentDir = path.dirname(filePath);
if (!fs.existsSync(parentDir)) {
fs.mkdirSync(parentDir, {recursive: true});
}
}Hier sehen wir ensureParentDirectory() in Aktion (Zeile A)
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
]
);
const filePath = 'dir/sub/subsub/new-file.txt';
ensureParentDirectory(filePath); // (A)
fs.writeFileSync(filePath, 'content', {encoding: 'utf-8'});
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
'dir/sub',
'dir/sub/subsub',
'dir/sub/subsub/new-file.txt',
]
);fs.mkdtempSync(pathPrefix, options?) erstellt ein temporäres Verzeichnis: Es hängt 6 zufällige Zeichen an pathPrefix an, erstellt ein Verzeichnis unter dem neuen Pfad und gibt diesen Pfad zurück.
pathPrefix sollte nicht mit einem Großbuchstaben „X“ enden, da einige Plattformen nachfolgende X durch zufällige Zeichen ersetzen.
Wenn wir unser temporäres Verzeichnis innerhalb eines betriebssystemspezifischen globalen temporären Verzeichnisses erstellen möchten, können wir die Funktion os.tmpdir() verwenden
import * as os from 'node:os';
import * as path from 'node:path';
const pathPrefix = path.resolve(os.tmpdir(), 'my-app');
// e.g. '/var/folders/ph/sz0384m11vxf/T/my-app'
const tmpPath = fs.mkdtempSync(pathPrefix);
// e.g. '/var/folders/ph/sz0384m11vxf/T/my-app1QXOXP'Es ist wichtig zu beachten, dass temporäre Verzeichnisse nicht automatisch gelöscht werden, wenn ein Node.js-Skript beendet wird. Wir müssen es entweder selbst löschen oder uns darauf verlassen, dass das Betriebssystem sein globales temporäres Verzeichnis regelmäßig bereinigt (was es tun kann oder auch nicht).
fs.cpSync(srcPath, destPath, options?): kopiert eine Datei oder ein Verzeichnis von srcPath nach destPath. Interessante Optionen
.recursive (Standard: false): Verzeichnisse (auch leere) werden nur kopiert, wenn diese Option true ist..force (Standard: true): Wenn true, werden vorhandene Dateien überschrieben. Wenn false, werden vorhandene Dateien beibehalten..errorOnExist auf true dazu, dass Fehler ausgelöst werden, wenn Dateipfade kollidieren..filter ist eine Funktion, mit der wir steuern können, welche Dateien kopiert werden..preserveTimestamps (Standard: false): Wenn true, erhalten die Kopien in destPath dieselben Zeitstempel wie die Originale in srcPath.Dies ist die Funktion in Aktion
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir-orig',
'dir-orig/some-file.txt',
]
);
fs.cpSync('dir-orig', 'dir-copy', {recursive: true});
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir-copy',
'dir-copy/some-file.txt',
'dir-orig',
'dir-orig/some-file.txt',
]
);Die Funktion traverseDirectory(dirPath) listet alle Nachkommen des Verzeichnisses unter dirPath auf.
fs.renameSync(oldPath, newPath) benennt eine Datei oder ein Verzeichnis von oldPath nach newPath um oder verschiebt es.
Verwenden wir diese Funktion, um ein Verzeichnis umzubenennen
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'old-dir-name',
'old-dir-name/some-file.txt',
]
);
fs.renameSync('old-dir-name', 'new-dir-name');
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'new-dir-name',
'new-dir-name/some-file.txt',
]
);Hier verwenden wir die Funktion, um eine Datei zu verschieben
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
'dir/subdir',
'dir/subdir/some-file.txt',
]
);
fs.renameSync('dir/subdir/some-file.txt', 'some-file.txt');
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
'dir/subdir',
'some-file.txt',
]
);Die Funktion traverseDirectory(dirPath) listet alle Nachkommen des Verzeichnisses unter dirPath auf.
rm, rm -r)fs.rmSync(thePath, options?) entfernt eine Datei oder ein Verzeichnis unter thePath. Interessante Optionen
.recursive (Standard: false): Verzeichnisse (auch leere) werden nur entfernt, wenn diese Option true ist..force (Standard: false): Wenn false, wird eine Ausnahme ausgelöst, wenn keine Datei oder kein Verzeichnis unter thePath vorhanden ist.Lassen Sie uns fs.rmSync() zum Entfernen einer Datei verwenden
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
'dir/some-file.txt',
]
);
fs.rmSync('dir/some-file.txt');
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
]
);Hier verwenden wir fs.rmSync(), um ein nicht leeres Verzeichnis rekursiv zu entfernen.
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
'dir/subdir',
'dir/subdir/some-file.txt',
]
);
fs.rmSync('dir/subdir', {recursive: true});
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
]
);Die Funktion traverseDirectory(dirPath) listet alle Nachkommen des Verzeichnisses unter dirPath auf.
rmdir)fs.rmdirSync(thePath, options?) entfernt ein leeres Verzeichnis (es wird eine Ausnahme ausgelöst, wenn ein Verzeichnis nicht leer ist).
Der folgende Code zeigt, wie diese Funktion funktioniert
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
'dir/subdir',
]
);
fs.rmdirSync('dir/subdir');
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
]
);Die Funktion traverseDirectory(dirPath) listet alle Nachkommen des Verzeichnisses unter dirPath auf.
Ein Skript, das seine Ausgabe in ein Verzeichnis dir speichert, muss dir oft vor Beginn leeren: Alle Dateien in dir entfernen, sodass es leer ist. Die folgende Funktion erledigt dies.
import * as path from 'node:path';
function clearDirectory(dirPath) {
for (const fileName of fs.readdirSync(dirPath)) {
const pathName = path.join(dirPath, fileName);
fs.rmSync(pathName, {recursive: true});
}
}Wir haben zwei Dateisystemfunktionen verwendet
fs.readdirSync(dirPath) gibt die Namen aller Kinder des Verzeichnisses unter dirPath zurück. Sie wird in §8.4.1 „Ein Verzeichnis durchlaufen“ erklärt.fs.rmSync(pathName, options?) entfernt Dateien und Verzeichnisse (auch nicht leere). Sie wird in §8.6.1 „Dateien und beliebige Verzeichnisse entfernen (Shell: rm, rm -r)“ erklärt.Dies ist ein Beispiel für die Verwendung von clearDirectory()
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
'dir/dir-file.txt',
'dir/subdir',
'dir/subdir/subdir-file.txt'
]
);
clearDirectory('dir');
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
]
);Die Bibliothek trash verschiebt Dateien und Ordner in den Papierkorb. Sie funktioniert unter macOS, Windows und Linux (wo die Unterstützung begrenzt ist und Hilfe benötigt wird). Dies ist ein Beispiel aus ihrer Readme-Datei
import trash from 'trash';
await trash(['*.png', '!rainbow.png']);trash() akzeptiert entweder ein Array von Strings oder einen einzelnen String als ersten Parameter. Jeder String kann ein Glob-Muster sein (mit Sternchen und anderen Metazeichen).
fs.existsSync(thePath) gibt true zurück, wenn eine Datei oder ein Verzeichnis unter thePath existiert
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
'dir/some-file.txt',
]
);
assert.equal(
fs.existsSync('dir'), true
);
assert.equal(
fs.existsSync('dir/some-file.txt'), true
);
assert.equal(
fs.existsSync('dir/non-existent-file.txt'), false
);Die Funktion traverseDirectory(dirPath) listet alle Nachkommen des Verzeichnisses unter dirPath auf.
fs.statSync(thePath, options?) gibt eine Instanz von fs.Stats mit Informationen über die Datei oder das Verzeichnis unter thePath zurück.
Interessante options
.throwIfNoEntry (Standard: true): Was passiert, wenn kein Eintrag unter path vorhanden ist?true ist, wird eine Ausnahme ausgelöst.false ist, wird undefined zurückgegeben..bigint (Standard: false): Wenn true, verwendet diese Funktion BigInts für numerische Werte (wie z. B. Zeitstempel, siehe unten).Eigenschaften von Instanzen von fs.Stats
stats.isFile()stats.isDirectory()stats.isSymbolicLink()stats.size ist die Größe in Bytesstats.atime: Zeitpunkt des letzten Zugriffsstats.mtime: Zeitpunkt der letzten Änderungstats.birthtime: Zeitpunkt der Erstellungatimestats.atime: Instanz von Datestats.atimeMS: Millisekunden seit der POSIX-Epochestats.atimeNs: Nanosekunden seit der POSIX-Epoche (erfordert die Option .bigint)Im folgenden Beispiel verwenden wir fs.statSync(), um eine Funktion isDirectory() zu implementieren
function isDirectory(thePath) {
const stats = fs.statSync(thePath, {throwIfNoEntry: false});
return stats !== undefined && stats.isDirectory();
}
assert.deepEqual(
Array.from(traverseDirectory('.')),
[
'dir',
'dir/some-file.txt',
]
);
assert.equal(
isDirectory('dir'), true
);
assert.equal(
isDirectory('dir/some-file.txt'), false
);
assert.equal(
isDirectory('non-existent-dir'), false
);Die Funktion traverseDirectory(dirPath) listet alle Nachkommen des Verzeichnisses unter dirPath auf.
Werfen wir einen kurzen Blick auf Funktionen zum Ändern von Dateiattributen
fs.chmodSync(path, mode) ändert die Berechtigung einer Datei.fs.chownSync(path, uid, gid) ändert den Besitzer und die Gruppe einer Datei.fs.utimesSync(path, atime, mtime) ändert die Zeitstempel einer Dateiatime: Zeitpunkt des letzten Zugriffsmtime: Zeitpunkt der letzten ÄnderungFunktionen für die Arbeit mit Hardlinks
fs.linkSync(existingPath, newPath) erstellt einen Hardlink.fs.unlinkSync(path) entfernt einen Hardlink und möglicherweise die Datei, auf die er zeigt (wenn es der letzte Hardlink zu dieser Datei ist).Funktionen für die Arbeit mit symbolischen Links
fs.symlinkSync(target, path, type?) erstellt einen symbolischen Link von path zu target.fs.readlinkSync(path, options?) gibt das Ziel des symbolischen Links unter path zurück.Die folgenden Funktionen arbeiten mit symbolischen Links, ohne sie aufzulösen (beachten Sie das Präfix „l“)
fs.lchmodSync(path, mode) ändert die Berechtigungen des symbolischen Links unter path.fs.lchownSync(path, uid, gid) ändert Benutzer und Gruppe des symbolischen Links unter path.fs.lutimesSync(path, atime, mtime) ändert die Zeitstempel des symbolischen Links unter path.fs.lstatSync(path, options?) gibt die Statistiken (Zeitstempel usw.) des symbolischen Links unter path zurück.Weitere nützliche Funktionen
fs.realpathSync(path, options?) berechnet den kanonischen Pfadnamen, indem Punkte (.), doppelte Punkte (..) und symbolische Links aufgelöst werden.Optionen von Funktionen, die beeinflussen, wie symbolische Links behandelt werden
fs.cpSync(src, dest, options?):
.dereference (Standard: false): Wenn true, werden die Dateien kopiert, auf die symbolische Links zeigen, nicht die symbolischen Links selbst..verbatimSymlinks (Standard: false): Wenn false, wird das Ziel eines kopierten symbolischen Links aktualisiert, sodass es weiterhin auf denselben Speicherort verweist. Wenn true, wird das Ziel nicht geändert.