561 Stimmen

Verzeichnis entfernen, das nicht leer ist

In meiner Node-Anwendung muss ich ein Verzeichnis entfernen, das einige Dateien enthält, aber fs.rmdir funktioniert nur bei leeren Verzeichnissen. Wie kann ich das machen?

28voto

thybzi Punkte 1183

Meine modifizierte Antwort von @oconnecp (https://stackoverflow.com/a/25069828/3027390)

Verwendet path.join für eine bessere plattformübergreifende Erfahrung. Also vergiss nicht, es zu requirieren.

var path = require('path');

Habe die Funktion auch in rimraf umbenannt ;)

/**
 * Verzeichnis rekursiv entfernen
 * @param {string} dir_path
 * @see https://stackoverflow.com/a/42505874/3027390
 */
function rimraf(dir_path) {
    if (fs.existsSync(dir_path)) {
        fs.readdirSync(dir_path).forEach(function(entry) {
            var entry_path = path.join(dir_path, entry);
            if (fs.lstatSync(entry_path).isDirectory()) {
                rimraf(entry_path);
            } else {
                fs.unlinkSync(entry_path);
            }
        });
        fs.rmdirSync(dir_path);
    }
}

22voto

mdmundo Punkte 550

Von den Node-Dokumenten, wie man hier sehen kann.

Um ein Verhalten ähnlich dem Unix-Befehl rm -rf zu erhalten, verwenden Sie fs.rm() mit den Optionen { recursive: true, force: true }.

Zum Beispiel (ESM)

import { rm } from 'node:fs/promises';

await rm('/Pfad/zum', { recursive: true, force: true });

21voto

Sukima Punkte 9785

Ich belebe normalerweise alte Threads nicht wieder, aber hier gibt es viel zum Thema "Churn", und ohne die Antwort von rimraf erscheinen mir all diese Lösungen übermäßig kompliziert.

Zunächst können Sie in modernem Node (>= v8.0.0) den Prozess vereinfachen, indem Sie nur Node-Core-Module verwenden, vollständig asynchron sind und das Löschen von Dateien gleichzeitig parallelisieren, alles in einer Funktion von fünf Zeilen und dennoch die Lesbarkeit beibehalten:

const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const readdir = promisify(fs.readdir);
const rmdir = promisify(fs.rmdir);
const unlink = promisify(fs.unlink);

exports.rmdirs = async function rmdirs(dir) {
  let entries = await readdir(dir, { withFileTypes: true });
  await Promise.all(entries.map(entry => {
    let fullPath = path.join(dir, entry.name);
    return entry.isDirectory() ? rmdirs(fullPath) : unlink(fullPath);
  }));
  await rmdir(dir);
};

Zum anderen ist ein Schutz vor Pfad-Traversierungsangriffen für diese Funktion unangemessen, weil

  1. es außerhalb des Geltungsbereichs des Single Responsibility Principle liegt.
  2. von dem Aufrufer behandelt werden sollte nicht diese Funktion. Dies ist ähnlich wie der Befehlszeilenbefehl rm -rf in dem es ein Argument entgegennimmt und dem Benutzer erlaubt, rm -rf / auszuführen, wenn danach gefragt wird. Es wäre die Verantwortung eines Skripts, nicht des rm Programms selbst.
  3. diese Funktion nicht in der Lage wäre, einen solchen Angriff zu erkennen, da sie keinen Referenzrahmen hat. Wiederum liegt es in der Verantwortung des Aufrufers, der die Absicht des Kontextes hätte, was ihm einen Bezug zum Vergleich der Pfadtraversierung geben würde.
  4. Symlinks sind kein Problem, da .isDirectory() false für Symlinks ist und diese nicht rekursiv durchlaufen werden.

Zu guter Letzt gibt es eine seltene Race Condition, bei der die Rekursion einen Fehler zurückgeben könnte, wenn eines der Einträge außerhalb dieses Skripts zum richtigen Zeitpunkt gelöscht wurde, während diese Rekursion läuft. Da dieses Szenario in den meisten Umgebungen nicht typisch ist, kann es wahrscheinlich übersehen werden. Wenn jedoch erforderlich (für einige Randfälle), kann dieses Problem mit diesem etwas komplexeren Beispiel gemildert werden:

exports.rmdirs = async function rmdirs(dir) {
  let entries = await readdir(dir, { withFileTypes: true });
  let results = await Promise.all(entries.map(entry => {
    let fullPath = path.join(dir, entry.name);
    let task = entry.isDirectory() ? rmdirs(fullPath) : unlink(fullPath);
    return task.catch(error => ({ error }));
  }));
  results.forEach(result => {
    // Ignoriere fehlende Dateien/Verzeichnisse; breche bei anderen Fehlern ab
    if (result && result.error.code !== 'ENOENT') throw result.error;
  });
  await rmdir(dir);
};

EDIT: Mache isDirectory() zu einer Funktion. Lösche das tatsächliche Verzeichnis am Ende. Behebe fehlende Rekursion.

12voto

Tony Brix Punkte 3805

Hier ist eine asynchrone Version von @SharpCoder's Antwort

const fs = require('fs');
const path = require('path');

function deleteFile(dir, file) {
    return new Promise(function (resolve, reject) {
        var filePath = path.join(dir, file);
        fs.lstat(filePath, function (err, stats) {
            if (err) {
                return reject(err);
            }
            if (stats.isDirectory()) {
                resolve(deleteDirectory(filePath));
            } else {
                fs.unlink(filePath, function (err) {
                    if (err) {
                        return reject(err);
                    }
                    resolve();
                });
            }
        });
    });
};

function deleteDirectory(dir) {
    return new Promise(function (resolve, reject) {
        fs.access(dir, function (err) {
            if (err) {
                return reject(err);
            }
            fs.readdir(dir, function (err, files) {
                if (err) {
                    return reject(err);
                }
                Promise.all(files.map(function (file) {
                    return deleteFile(dir, file);
                })).then(function () {
                    fs.rmdir(dir, function (err) {
                        if (err) {
                            return reject(err);
                        }
                        resolve();
                    });
                }).catch(reject);
            });
        });
    });
};

12voto

oconnecp Punkte 702

Ich habe diese Funktion namens "removeFolder" geschrieben. Sie entfernt rekursiv alle Dateien und Ordner an einem bestimmten Speicherort. Das einzige Paket, das dafür erforderlich ist, ist async.

var async = require('async');

function removeFolder(location, next) {
    fs.readdir(location, function (err, files) {
        async.each(files, function (file, cb) {
            file = location + '/' + file
            fs.stat(file, function (err, stat) {
                if (err) {
                    return cb(err);
                }
                if (stat.isDirectory()) {
                    removeFolder(file, cb);
                } else {
                    fs.unlink(file, function (err) {
                        if (err) {
                            return cb(err);
                        }
                        return cb();
                    })
                }
            })
        }, function (err) {
            if (err) return next(err)
            fs.rmdir(location, function (err) {
                return next(err)
            })
        })
    })
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X