在我的节点应用程序中,我需要删除一个目录,其中有一些文件,但fs。Rmdir只适用于空目录。我该怎么做呢?


当前回答

我通常不复活旧线程,但这里有很多关于搅动和没有rimraf的答案,这些对我来说似乎都太复杂了。

首先,在现代Node (>= v8.0.0)中,你可以只使用节点核心模块来简化过程,完全异步,并在5行函数中并行化文件的解链接,并且仍然保持可读性:

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

另一方面,路径遍历攻击的保护不适合此函数,因为

It is out of scope based on the Single Responsibility Principle. Should be handled by the caller not this function. This is akin to the command-line rm -rf in 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 rm program itself. 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. Sym-links are not a concern as .isDirectory() is false for sym-links and are unlinked not recursed into.

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

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

编辑:使isDirectory()成为一个函数。最后删除实际目录。修复丢失的递归。

其他回答

有一个模块叫做rimraf (https://npmjs.org/package/rimraf)。它提供了与rm -Rf相同的功能

异步用法:

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

同步用法:

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

只需使用rmdir模块!这很简单。

这是一种使用promisify和两个帮助函数(to和toAll)来解决承诺的方法。

它以异步方式执行所有操作。

const fs = require('fs');
const { promisify } = require('util');
const to = require('./to');
const toAll = require('./toAll');

const readDirAsync = promisify(fs.readdir);
const rmDirAsync = promisify(fs.rmdir);
const unlinkAsync = promisify(fs.unlink);

/**
    * @author Aécio Levy
    * @function removeDirWithFiles
    * @usage: remove dir with files
    * @param {String} path
    */
const removeDirWithFiles = async path => {
    try {
        const file = readDirAsync(path);
        const [error, files] = await to(file);
        if (error) {
            throw new Error(error)
        }
        const arrayUnlink = files.map((fileName) => {
            return unlinkAsync(`${path}/${fileName}`);
        });
        const [errorUnlink, filesUnlink] = await toAll(arrayUnlink);
        if (errorUnlink) {
            throw new Error(errorUnlink);
        }
        const deleteDir = rmDirAsync(path);
        const [errorDelete, result] = await to(deleteDir);
        if (errorDelete) {
            throw new Error(errorDelete);
        }
    } catch (err) {
        console.log(err)
    }
}; 
const fs = require("fs")
const path = require("path")

let _dirloc = '<path_do_the_directory>'

if (fs.existsSync(_dirloc)) {
  fs.readdir(path, (err, files) => {
    if (!err) {
      for (let file of files) {
        // Delete each file
        fs.unlinkSync(path.join(_dirloc, file))
      }
    }
  })
  // After the 'done' of each file delete,
  // Delete the directory itself.
  if (fs.unlinkSync(_dirloc)) {
    console.log('Directory has been deleted!')
  }
}

如果你正在使用的节点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);
    }
}