430

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

4

33 回答 33

472

从 Node.js 14.14.0 开始,推荐的方法是使用fs.rmSync

fs.rmSync(dir, { recursive: true, force: true });
于 2019-09-10T07:41:26.337 回答
383

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

异步用法:

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

同步使用:

rimraf.sync("/some/directory");
于 2013-08-05T07:43:23.333 回答
286

同步删除文件夹

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

    const deleteFolderRecursive = function (directoryPath) {
    if (fs.existsSync(directoryPath)) {
        fs.readdirSync(directoryPath).forEach((file, index) => {
          const curPath = path.join(directoryPath, file);
          if (fs.lstatSync(curPath).isDirectory()) {
           // recurse
            deleteFolderRecursive(curPath);
          } else {
            // delete file
            fs.unlinkSync(curPath);
          }
        });
        fs.rmdirSync(directoryPath);
      }
    };
于 2015-08-25T06:56:30.477 回答
187

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

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

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

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

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

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

fs.removeSync('/tmp/myFolder'); 
//or
fs.remove('/tmp/myFolder', callback);
于 2016-04-29T09:13:29.807 回答
29

从 Node v14(2020 年 10 月)开始,该fs模块具有fs.rm并且rs.rmSync支持递归、非空目录取消链接:

https://nodejs.org/docs/latest-v14.x/api/fs.html#fs_fs_rm_path_options_callback

所以你现在可以做这样的事情:

const fs = require('fs');
fs.rm('/path/to/delete', { recursive: true }, () => console.log('done'));

或者:

const fs = require('fs');
fs.rmSync('/path/to/delete', { recursive: true });
console.log('done');
于 2021-01-25T16:36:18.553 回答
26

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

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

var path = require('path');

也将函数重命名为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);
    }
}
于 2017-02-28T10:13:38.577 回答
21

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

首先在现代 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);
};

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

  1. 它超出了单一职责原则的范围。
  2. 应该由调用者而不是这个函数来处理。这类似于命令行rm -rf,因为它需要一个参数,并允许用户在rm -rf /被要求时使用。脚本的职责不是保护rm程序本身。
  3. 此函数将无法确定此类攻击,因为它没有参考框架。这也是调用者的责任,调用者将拥有意图的上下文,这将为它提供一个参考来比较路径遍历。
  4. .isDirectory()符号链接不像符号链接那样是一个问题,false并且没有链接,也没有递归。

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

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()一个功能。最后删除实际目录。修复丢失的递归。

于 2019-04-13T03:51:53.280 回答
12

这是@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);
            });
        });
    });
};
于 2016-12-09T04:06:06.813 回答
10

我写了这个叫做删除文件夹的函数。它将递归删除某个位置的所有文件和文件夹。它唯一需要的包是异步的。

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)
            })
        })
    })
}
于 2014-07-31T22:15:42.020 回答
10

[编辑:使用 node.js v15.5.0]

刚刚尝试使用此处发布的一些解决方案,我遇到了以下弃用警告:

(node:13202) [DEP0147] DeprecationWarning:在 Node.js 的未来版本中,如果 path 不存在或者是文件,fs.rmdir(path, { recursive: true }) 将抛出。改用 fs.rm(path, { recursive: true, force: true })

fs.rm(path, { recursive: true, force: true });fs.rmSync(path, { recursive: true, force: true });如果你想使用阻塞版本,效果很好。

于 2021-01-23T13:45:35.053 回答
8

如果您使用的是 node 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);
    }
}
于 2018-02-27T10:39:09.523 回答
7
const fs = require("fs");
fs.rmdir("./test", { recursive: true }, (err) => {
  if (err) {
    console.error(err);
  }
});

提供recursive: true选项。它将递归删除给定路径的所有文件和目录。(假设test目录存在于根目录。)

于 2020-07-26T11:21:19.300 回答
4

我在试图克服困难时到达这里,gulp我正在写信以求更进一步。

当您想使用 删除文件和文件夹del时,您应该追加/**以进行递归删除。

gulp.task('clean', function () {
    return del(['some/path/to/delete/**']);
});
于 2016-02-29T11:21:28.637 回答
4

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

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);
    }
};
于 2018-07-25T13:58:04.790 回答
4

在最新版本的 Node.js(12.10.0 或更高版本)中,rmdir样式函数fs.rmdir()fs.rmdirSync()fs.promises.rmdir()具有recursive允许删除非空目录的新实验选项,例如

fs.rmdir(path, { recursive: true });

GitHub上的相关PR:https ://github.com/nodejs/node/pull/29168

于 2019-09-07T13:31:45.790 回答
4

根据fs文档fsPromises目前recursive在实验基础上提供了该选项,至少在我自己的 Windows 上,它会删除目录和其中的任何文件。

fsPromises.rmdir(path, {
  recursive: true
})

是否recursive: true删除 Linux 和 MacOS 上的文件?

于 2020-02-11T02:53:27.220 回答
4

解释

从 Node.js v14 开始,我们现在可以使用该require("fs").promises.rm函数通过 Promise 删除文件。第一个参数将是要删除的文件或文件夹(甚至是不存在的文件或文件夹)。您可以在第二个参数的对象中使用recursiveforce选项来模拟带有选项的rmShell 命令实用程序的行为。-rf

例子

"use strict";

require("fs").promises.rm("directory", {recursive: true, force: true}).then(() => {
  console.log("removed");
}).catch(error => {
  console.error(error.message);
});

Node.js v14 文档

Mozilla 开发者承诺文档

rm命令手册页

于 2021-06-01T08:06:07.263 回答
3
return new Promise((resolve, reject) => {
  const fs = require("fs");
  // directory path
  const dir = "your/dir";

  // delete directory recursively <------
  fs.rmdir(dir, { recursive: true }, (err) => {
    if (err) {
      reject(err);
    }
    resolve(`${dir} is deleted!`);
  });
});
于 2020-11-15T17:26:09.600 回答
3

如此处所写。

要获得类似于rm -rfUnix 命令的行为,请使用fs.rm()with options { recursive: true, force: true }

于 2021-10-21T15:14:04.103 回答
2

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

于 2015-09-14T11:23:04.353 回答
2

事实上的包是rimraf,但这是我的小异步版本:

const fs = require('fs')
const path = require('path')
const Q = require('q')

function rmdir (dir) {
  return Q.nfcall(fs.access, dir, fs.constants.W_OK)
    .then(() => {
      return Q.nfcall(fs.readdir, dir)
        .then(files => files.reduce((pre, f) => pre.then(() => {
          var sub = path.join(dir, f)
          return Q.nfcall(fs.lstat, sub).then(stat => {
            if (stat.isDirectory()) return rmdir(sub)
            return Q.nfcall(fs.unlink, sub)
          })
        }), Q()))
    })
    .then(() => Q.nfcall(fs.rmdir, dir))
}

于 2017-12-21T14:27:25.590 回答
2

一种快速而肮脏的方法(可能用于测试)可能是直接使用execorspawn方法来调用操作系统调用以删除目录。阅读更多关于NodeJs child_process的信息。

let exec = require('child_process').exec
exec('rm -Rf /tmp/*.zip', callback)

缺点是:

  1. 您取决于底层操作系统,即相同的方法将在 unix/linux 中运行,但可能不在 Windows 中。
  2. 您不能根据条件或错误劫持流程。您只需将任务交给底层操作系统并等待退出代码返回。

好处:

  1. 这些进程可以异步运行。
  2. 您可以监听命令的输出/错误,因此命令输出不会丢失。如果操作未完成,您可以检查错误代码并重试。
于 2018-02-26T03:29:32.127 回答
2

2020 更新

从 12.10.0 版本开始,为选项添加了recursiveOption 。

请注意,递归删除是实验性的

所以你会做同步:

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

或异步:

fs.rmdir(dir, {recursive: true});
于 2020-01-02T20:04:12.437 回答
1

超高速和防故障

您可以使用该lignator软件包(https://www.npmjs.com/package/lignator),它比任何异步代码(例如 rimraf)都更快,并且更防故障(尤其是在 Windows 中,文件删除不是即时的,文件可能被其他进程锁定)。

Windows 上 4,36 GB 数据、28 042 个文件、4 217 个文件夹在15 秒内删除,而 rimraf在旧硬盘上则需要 60 秒。

const lignator = require('lignator');

lignator.remove('./build/');
于 2019-08-03T17:09:48.780 回答
1

将文件夹与文件同步删除或仅删除一个文件。

我既不是给予者也不是贡献者,但我找不到解决这个问题的好方法,我必须找到自己的方法......所以我希望你会喜欢它:)

非常适合我使用任何数量的嵌套目录和子目录。递归函数时注意“this”的范围,您的实现可能会有所不同。在我的情况下,这个函数停留在另一个函数的返回中,这就是我用这个调用它的原因。

    const fs = require('fs');

    deleteFileOrDir(path, pathTemp = false){
            if (fs.existsSync(path)) {
                if (fs.lstatSync(path).isDirectory()) {
                    var files = fs.readdirSync(path);
                    if (!files.length) return fs.rmdirSync(path);
                    for (var file in files) {
                        var currentPath = path + "/" + files[file];
                        if (!fs.existsSync(currentPath)) continue;
                        if (fs.lstatSync(currentPath).isFile()) {
                            fs.unlinkSync(currentPath);
                            continue;
                        }
                        if (fs.lstatSync(currentPath).isDirectory() && !fs.readdirSync(currentPath).length) {
                            fs.rmdirSync(currentPath);
                        } else {
                            this.deleteFileOrDir(currentPath, path);
                        }
                    }
                    this.deleteFileOrDir(path);
                } else {
                    fs.unlinkSync(path);
                }
            }
            if (pathTemp) this.deleteFileOrDir(pathTemp);
        }
于 2019-08-07T06:44:09.870 回答
1

2020答案

如果您想在npm 脚本中执行此操作,如果您使用该命令,则无需预先安装任何 3rd 方包npx

例如,如果您想在运行时删除文件夹dist.cachenpm run clean ,那么只需将此命令添加到您的package.json

{
  "scripts": {
    "clean": "npx rimraf dist .cache"
  }
}

它适用于任何操作系统

于 2020-11-22T01:53:53.353 回答
0

我希望有一种方法可以做到这一点,而无需额外的模块来处理如此微小和常见的事情,但这是我能想到的最好的方法。

更新:现在应该可以在 Windows(经过测试的 Windows 10)上运行,也应该可以在 Linux/Unix/BSD/Mac 系统上运行。

const
    execSync = require("child_process").execSync,
    fs = require("fs"),
    os = require("os");

let removeDirCmd, theDir;

removeDirCmd = os.platform() === 'win32' ? "rmdir /s /q " : "rm -rf ";

theDir = __dirname + "/../web-ui/css/";

// WARNING: Do not specify a single file as the windows rmdir command will error.
if (fs.existsSync(theDir)) {
    console.log(' removing the ' + theDir + ' directory.');
    execSync(removeDirCmd + '"' + theDir + '"', function (err) {
        console.log(err);
    });
}
于 2016-09-04T12:35:41.983 回答
0

另一种选择是使用fs-promise提供模块的承诺版本的fs-extra模块

然后你可以这样写:

const { remove, mkdirp, writeFile, readFile } = require('fs-promise')
const { join, dirname } = require('path')

async function createAndRemove() {
  const content = 'Hello World!'
  const root = join(__dirname, 'foo')
  const file = join(root, 'bar', 'baz', 'hello.txt')

  await mkdirp(dirname(file))
  await writeFile(file, content)
  console.log(await readFile(file, 'utf-8'))
  await remove(join(__dirname, 'foo'))
}

createAndRemove().catch(console.error)

注意:async/await 需要最新的 nodejs 版本(7.6+)

于 2017-03-30T14:16:49.607 回答
0
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!')
  }
}
于 2018-09-26T07:06:01.310 回答
0

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

它异步执行所有操作。

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)
    }
}; 
于 2018-10-22T14:18:54.637 回答
0

//不使用任何第三方库

const fs = require('fs');
var FOLDER_PATH = "./dirname";
var files = fs.readdirSync(FOLDER_PATH);
files.forEach(element => {
    fs.unlinkSync(FOLDER_PATH + "/" + element);
});
fs.rmdirSync(FOLDER_PATH);
于 2019-08-20T05:09:40.420 回答
0

Whilerecursive是一个实验选项fs.rmdir

function rm (path, cb) {
    fs.stat(path, function (err, stats) {
        if (err)
            return cb(err);

        if (stats.isFile())
            return fs.unlink(path, cb);

        fs.rmdir(path, function (err) {
            if (!err || err && err.code != 'ENOTEMPTY') 
                return cb(err);

            fs.readdir(path, function (err, files) {
                if (err)
                    return cb(err);

                let next = i => i == files.length ? 
                    rm(path, cb) : 
                    rm(path + '/' + files[i], err => err ? cb(err) : next(i + 1));

                next(0);
            });
        });
    });
}
于 2019-10-18T16:20:34.110 回答
0

❄️你可以使用graph-fs

directory.delete()
于 2020-07-13T10:38:31.307 回答