删除非空的目录

时间:2013-08-05 07:31:12

标签: node.js filesystems

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

30 个答案:

答案 0 :(得分:230)

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

相同的功能

异步用法:

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

同步用法:

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

答案 1 :(得分:125)

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

  

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

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

替换fs
// 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);

答案 2 :(得分:46)

截至2019年...

使用Node.js 12.10,您最终可以做一个简单的事情:

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

recursive选项会递归删除整个内容。

答案 3 :(得分:19)

自 Node v14(2020 年 10 月)起,fs 模块具有支持递归非空目录取消链接的 fs.rmrs.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');

答案 4 :(得分:18)

我从@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);
    }
}

答案 5 :(得分:10)

以下是@SharpCoder's answer

的异步版本
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);
            });
        });
    });
};

答案 6 :(得分:9)

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

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

答案 7 :(得分:8)

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

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

<块引用>

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

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

答案 8 :(得分:6)

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

答案 9 :(得分:6)

const fs = require("fs");
fs.rmdir("./test", { recursive: true }, (err) => {
  if (err) {
    console.error(err);
  }
});

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

答案 10 :(得分:3)

我试图通过gulp来到这里,并且我正在为更远的距离写作。

如果要使用del删除文件和文件夹,则应附加/**以进行递归删除。

gulp.task('clean', function () {
    return del(['some/path/to/delete/**']);
});

答案 11 :(得分:3)

根据fs documentationfsPromises当前在实验的基础上提供了recursive选项,至少就我个人而言,在Windows上,它会删除目录及其中的任何文件。

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

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

答案 12 :(得分:2)

在最新版本的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

答案 13 :(得分:2)

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

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

答案 14 :(得分:2)

我希望有一种方法可以做到这一点,没有额外的模块,这些东西是如此微不足道和常见,但这是我能想到的最好的。

更新: 现在应该可以在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);
    });
}

答案 15 :(得分:1)

2020更新

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

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

所以您要进行同步:

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

或异步:

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

答案 16 :(得分:1)

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

答案 17 :(得分:1)

static void ProcessAnswer(string[] array) { string action = array.GetValue(0).ToString(); value = array.GetValue(1).ToString(); string c = keyaction[action](); // calling the referenced funtion Console.Write(c); } recursive的实验选项

fs.rmdir

答案 18 :(得分:1)

超速和防故障

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

  

在Windows上的 15秒内删除了4,36 GB的数据,28,042个文件,4,217个文件夹,而旧硬盘上rimraf的数据是60秒

const lignator = require('lignator');

lignator.remove('./build/');

答案 19 :(得分: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);
        }

答案 20 :(得分:1)

我通常不复活旧线程,但是这里有一个 lot ,而且没有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 });
  return Promise.all(entries.map(entry => {
    let fullPath = path.join(dir, entry.name);
    return entry.isDirectory ? rmdir(fullPath) : unlink(fullPath);
  }));
};

另一方面,path traversal attacks的警卫不适用于此功能,因为

  1. 根据Single Responsibility Principle,它不在范围内。
  2. 应由调用方而非此函数处理。这与命令行rm -rf类似,它接受一个参数,并允许用户rm -rf /进行输入。脚本的职责是不要保护rm程序本身。
  3. 该功能将无法确定此类攻击,因为它没有参考系。同样,这是调用者的责任,调用者将具有意图的上下文,这将为它提供比较路径遍历的参考。
  4. 符号链接不是问题,因为符号链接的.isDirectoryfalse,并且未链接也不会出现。

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

exports.rmdirs = async function rmdirs(dir) {
  let entries = await readdir(dir, { withFileTypes: true });
  let results = Promise.all(entries.map(entry => {
    let fullPath = path.join(dir, entry.name);
    let task = entry.isDirectory ? rmdir(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;
  });
};

答案 21 :(得分:1)

Promisified version:

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

function rmdir(dir) {
  return Q.nfcall(fs.access, dir).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()))
  }, err => {})
  .then(() => Q.nfcall(fs.rmdir, dir))
}

答案 22 :(得分: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);

答案 23 :(得分:0)

这是一种使用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)
    }
}; 

答案 24 :(得分:0)

?graph-fs


安装

npm i graph-fs

使用

const {Node} = require("graph-fs");
const directory = new Node("/path/to/directory");

directory.delete(); // <--

答案 25 :(得分: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!')
  }
}

答案 26 :(得分:0)

快速而肮脏的方式(可能用于测试)可能是直接使用execspawn方法来调用OS调用来删除目录。详细了解NodeJs child_process

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

缺点是:

  1. 您依赖于底层操作系统,即同样的方法可以在unix / linux中运行,但可能不在Windows中。
  2. 你不能在条件或错误上劫持这个过程。您只需将任务提供给底层操作系统并等待退出退出代码。
  3. <强>优点:

    1. 这些进程可以异步运行。
    2. 您可以侦听命令的输出/错误,因此命令输出不会丢失。如果操作未完成,您可以检查错误代码并重试。

答案 27 :(得分:0)

2020年答案

如果要在 npm脚本中执行此操作,则使用命令npx

无需安装任何第三方软件包。

例如,如果您要在运行npm run clean时删除文件夹 dist .cache ,则只需将此命令添加到软件包中.json

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

它将在任何操作系统中工作

答案 28 :(得分: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 +)

答案 29 :(得分:0)

说明

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

示例

"use strict";

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

Node.js v14 Documentation

Mozilla Developer Promises Documentation

rm command manual page