node.js 删除非空目录

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18052762/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 15:12:31  来源:igfitidea点击:

Remove directory which is not empty

node.jsfilesystems

提问by sachin

In my Node application I need to remove a directory which has some files, but fs.rmdironly works on empty directories. How can I do this?

在我的 Node 应用程序中,我需要删除一个包含一些文件的目录,但fs.rmdir仅适用于空目录。我怎样才能做到这一点?

回答by Morgan ARR Allen

There is a module for this called rimraf(https://npmjs.org/package/rimraf). It provides the same functionality as rm -Rf

有一个名为rimrafhttps://npmjs.org/package/rimraf)的模块。它提供与rm -Rf

Asyncusage:

异步用法:

var rimraf = require("rimraf");
rimraf("/some/directory", function () { console.log("done"); });

Syncusage:

同步用法:

rimraf.sync("/some/directory");

回答by SharpCoder

To remove folder synchronously

同步删除文件夹

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

const deleteFolderRecursive = function(path) {
  if (fs.existsSync(path)) {
    fs.readdirSync(path).forEach((file, index) => {
      const curPath = Path.join(path, file);
      if (fs.lstatSync(curPath).isDirectory()) { // recurse
        deleteFolderRecursive(curPath);
      } else { // delete file
        fs.unlinkSync(curPath);
      }
    });
    fs.rmdirSync(path);
  }
};

回答by Pierre Maoui

Most of the people using fswith Node.js would like functions close to the "Unix way" of dealing with files. I'm using fs-extrato bring all the cool stuff :

大多数使用fsNode.js的人都喜欢接近“Unix 方式”处理文件的功能。我正在使用fs-extra来带来所有很酷的东西:

fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf.

fs-extra 包含未包含在 vanilla Node.js fs 包中的方法。如 mkdir -p、cp -r 和 rm -rf。

Even better, fs-extrais a drop in replacement for native fs. All methods in fs are unmodified and attached to it. It means that you can replace fs by fs-extra:

更好的是,fs-extra是原生 fs 的替代品。fs 中的所有方法都未修改并附加到它。这意味着您可以用fs-extra替换 fs :

// this can be replaced
const fs = require('fs')

// by this
const fs = require('fs-extra')

And then you can remove a folder this way:

然后您可以通过以下方式删除文件夹:

fs.removeSync('/tmp/myFolder'); 
//or
fs.remove('/tmp/myFolder', callback);

回答by Pierre Maoui

As of 2019...

截至2019年...

As of Node.js 12.10.0, fs.rmdirSyncsupports a recursiveoptions, so you can finally do:

Node.js 12.10.0 开始fs.rmdirSync支持recursive选项,因此您最终可以执行以下操作:

fs.rmdirSync(dir, { recursive: true });

Where the recursiveoption deletes the entire directory recursively.

recursive选项以递归方式删除整个目录。

回答by thybzi

My modified answer from @oconnecp (https://stackoverflow.com/a/25069828/3027390)

我从@oconnecp 修改后的答案(https://stackoverflow.com/a/25069828/3027390

Uses path.join for better cross-platform experience. So, don't forget to require it.

使用 path.join 以获得更好的跨平台体验。所以,不要忘记要求它。

var path = require('path');

Also renamed function to rimraf;)

还将函数重命名为rimraf;)

/**
 * Remove directory recursively
 * @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);
    }
}

回答by Sukima

I don't usually resurrect old threads but there is a loton churn here and sans the rimraf answer these all seem overly complicated to me.

我通常不会复活旧线程,但是这里有很多流失并且没有 rimraf 答案,这些对我来说似乎都过于复杂。

First in modern Node (>= v8.0.0) you can simplify the process using only node core modules, fully asynchronous, and parallelize the unlinking of files concurrently all in a function of five lines and still keep readability:

首先在现代 Node (>= v8.0.0) 中,您可以仅使用节点核心模块来简化流程,完全异步,并在五行功能中并行并行化文件的取消链接,并且仍然保持可读性:

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);
};

On another note a guard for path traversal attacksis inappropriate for this function because

另一方面,路径遍历攻击的保护不适用于此功能,因为

  1. It is out of scope based on the Single Responsibility Principle.
  2. Should be handled by the caller notthis function. This is akin to the command-line rm -rfin that it takes an argument and will allow the user to rm -rf /if asked to. It would be the responsibility of a script to guard not the rmprogram itself.
  3. This function would be unable to determine such an attack since it does not have a frame of reference. Again that is the responsibility of the caller who would have the context of intent which would provide it a reference to compare the path traversal.
  4. Sym-links are not a concern as .isDirectory()is falsefor sym-links and are unlinked not recursed into.
  1. 它超出了基于单一职责原则的范围。
  2. 应该由调用者处理而不是这个函数。这类似于命令行rm -rf,因为它接受一个参数,并允许用户在rm -rf /被要求时使用。脚本的责任不是保护rm程序本身。
  3. 此函数将无法确定此类攻击,因为它没有参考系。同样,这是调用者的责任,他将拥有意图的上下文,这将为它提供比较路径遍历的参考。
  4. 符号链接是不是一个问题,因为.isDirectory()false对符号链接和取消关联不递归到。

Last but not least, there is a rare race condition that the recursion could error if one of the entries was unlinked or deleted outsidethis script at just the right time while this recursion is running. Since this scenario is not typical in most environments it can likely be overlooked. However, if required (for some edge cases) this issue can be mitigated with this slightly more complex example:

最后但并非最不重要的是,存在一种罕见的竞争条件,如果其中一个条目在此递归运行的正确时间在此脚本之外取消链接或删除,则递归可能会出错。由于这种情况在大多数环境中并不常见,因此很可能会被忽略。但是,如果需要(对于某些边缘情况),这个问题可以通过这个稍微复杂的例子来缓解:

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 => {
    // Ignore missing files/directories; bail on other errors
    if (result && result.error.code !== 'ENOENT') throw result.error;
  });
  await rmdir(dir);
};

EDIT:Make isDirectory()a function. Remove the actual directory at the end. Fix missing recursion.

编辑:isDirectory()一个功能。最后删除实际目录。修复丢失的递归。

回答by Tony Brix

Here is an async version of @SharpCoder's answer

这是@SharpCoder 答案的异步版本

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);
            });
        });
    });
};

回答by oconnecp

I wrote this function called remove folder. It will recursively remove all the files and folders in a location. The only package it requires is async.

我写了这个名为删除文件夹的函数。它将递归删除一个位置中的所有文件和文件夹。它唯一需要的包是 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)
            })
        })
    })
}

回答by RonZ

If you are using node 8+ want asyncronicity and don't want external dependencies, here is the async/await version:

如果您使用的节点 8+ 想要异步并且不想要外部依赖,这里是 async/await 版本:

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

const readdir = util.promisify(fs.readdir);
const lstat = util.promisify(fs.lstat);
const unlink = util.promisify(fs.unlink);
const rmdir = util.promisify(fs.rmdir);

const removeDir = async (dir) => {
    try {
        const files = await readdir(dir);
        await Promise.all(files.map(async (file) => {
            try {
                const p = path.join(dir, file);
                const stat = await lstat(p);
                if (stat.isDirectory()) {
                    await removeDir(p);
                } else {
                    await unlink(p);
                    console.log(`Removed file ${p}`);
                }
            } catch (err) {
                console.error(err);
            }
        }))
        await rmdir(dir);
        console.log(`Removed dir ${dir}`);
    } catch (err) {
      console.error(err);
    }
}

回答by Error404

Async version of @SharpCoder's answerusing fs.promises:

@SharpCoder使用 fs.promises回答的异步版本:

const fs = require('fs');
const afs = fs.promises;

const deleteFolderRecursive = async path =>  {
    if (fs.existsSync(path)) {
        for (let entry of await afs.readdir(path)) {
            const curPath = path + "/" + entry;
            if ((await afs.lstat(curPath)).isDirectory())
                await deleteFolderRecursive(curPath);
            else await afs.unlink(curPath);
        }
        await afs.rmdir(path);
    }
};