83

fs.rmdir的文档非常简短,并且没有解释 rmdir 在目录不为空时的行为。

:如果我尝试使用此 API 删除非空目录会怎样?

4

23 回答 23

96

虽然使用第三方库来做这样的事情,但我想不出更优雅的解决方案。所以我最终使用了 npm-module rimraf

安装它

npm install rimraf

或者安装它并保存到“package.json”(其他保存选项可以在npm-install文档中找到)

npm install --save rimraf

然后您可以执行以下操作:

rmdir = require('rimraf');
rmdir('some/directory/with/files', function(error){});

或者在 Coffeescript 中:

rmdir = require 'rimraf'
rmdir 'some/directory/with/files', (error)->
于 2013-05-17T09:05:36.017 回答
59

我写的正是这个问题

下面我之前的解决方案虽然简单,但不是首选。以下函数,是一个同步解决方案;而异步可能是首选。

deleteFolderRecursive = function(path) {
    var files = [];
    if( fs.existsSync(path) ) {
        files = fs.readdirSync(path);
        files.forEach(function(file,index){
            var curPath = path + "/" + file;
            if(fs.lstatSync(curPath).isDirectory()) { // recurse
                deleteFolderRecursive(curPath);
            } else { // delete file
                fs.unlinkSync(curPath);
            }
        });
        fs.rmdirSync(path);
    }
};

[编辑]添加 lstat 而不是 stat 以防止符号链接上的错误

[上一个解决方案]

我的解决方案很容易实现。

var exec = require('child_process').exec,child;
child = exec('rm -rf test',function(err,out) { 
  console.log(out); err && console.log(err); 
});

这个页面被精简了,但基本的想法很简单;在命令行上执行“rm -r”。如果您的应用程序需要在不同类型的操作系统上运行,请将其放在一个函数中并使用 if/else/switch 来处理它。

您将要处理所有响应;但这个想法很简单。

于 2012-10-06T17:09:48.880 回答
38

简短回答:node.js fs.rmdir()调用 POSIX rmdir();这将删除一个空目录,或者返回一个错误。在给定的情况下,调用将调用回调函数并将错误作为异常传递。

这里的问题是 node.js 文档引用了POSIX

Node.js API 文档 文件系统API 最初是

标准 POSIX 函数的简单包装器。

这几乎将问题变成了以下问题的副本: 是否有 POSIX API / 函数的列表?

的描述fs.rmdir很简洁,但足够了。

异步 rmdir(2)。

这里rmdir(2)是对文档的隐式引用rmdir() system call。这里的数字 (2) 是旧的 unix 手册页约定,表示手册页的第 2 节,包含内核接口。

于 2012-09-27T23:19:47.997 回答
22

Node.js v12.10.0 将recursive选项引入fs.rmdir. 由于fs.mkdir从 v10.12.0 开始支持相同的选项,因此可以递归地执行创建和删除目录。

$ node --experimental-repl-await

# without recursive option -> error
> await fs.promises.mkdir('foo/bar')
Thrown:
[Error: ENOENT: no such file or directory, mkdir 'foo/bar'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'mkdir',
  path: 'foo/bar'
}

# with recursive option -> success
> await fs.promises.mkdir('foo/bar', { recursive: true })
undefined

# without recursive option -> error
> await fs.promises.rmdir('foo')
Thrown:
[Error: ENOTEMPTY: directory not empty, rmdir 'foo'] {
  errno: -66,
  code: 'ENOTEMPTY',
  syscall: 'rmdir',
  path: 'foo'
}

# with recursive option -> success
> await fs.promises.rmdir('foo', { recursive: true })
undefined
于 2019-09-05T12:19:22.847 回答
11

这对我有用

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

2021年编辑:

现在它似乎已在 v14 中被替换为:

fs.rmSync('./output', {recursive: true, force: true});
于 2020-07-17T14:51:21.417 回答
7

只是这一堆答案中的一个小点,但我认为指出这一点很好。

就个人而言(并且通常),我更愿意使用已经存在的库(如果有可用的库)来完成任务。对我来说,尤其是在开源世界中,采用一个已经存在的东西意味着使用和改进一个已经存在的东西,这最终可能比我自己做更好的结果(我正在改进其他人拥有的东西)完毕)。

在这种情况下,通过一个小的搜索,我发现了模块 fs-extra,它旨在替代rimraf并解决递归删除目录的需要(显然是异步和同步版本)。此外,它在 github 上获得了很多星,并且目前似乎得到了维护:这两个条件,除了可以满足需求的事实外,对我来说也是可行的(几乎是一段时间)。

于 2014-04-02T08:39:44.667 回答
5

fs.rmdir不是递归的

您可以改为使用像readdirp这样的递归 fs.readdir 模块来查找所有文件和目录。然后删除所有文件,然后是所有目录。

如需更简单的解决方案,请查看rimraf

于 2012-09-27T20:57:21.560 回答
3

使用 child_process.execFile更快

NodeJS 文档:

child_process.execFile 类似于 child_process.exec() ,只是它*不执行子shell,而是直接执行指定的文件。

这行得通。模仿rm -rf DIR...

var child = require('child_process');

var rmdir = function(directories, callback) {
    if(typeof directories === 'string') {
        directories = [directories];
    }
    var args = directories;
    args.unshift('-rf');
    child.execFile('rm', args, {env:process.env}, function(err, stdout, stderr) {
            callback.apply(this, arguments);
    });
};

// USAGE
rmdir('dir');
rmdir('./dir');
rmdir('dir/*');
rmdir(['dir1', 'dir2']);

编辑:我不得不承认这不是跨平台的,在 Windows 上不起作用

于 2013-05-04T17:03:14.467 回答
3

从节点 v16 开始,recursivefs.rmdir现在已弃用。替换是fs.rm.

与承诺一起使用:

const fs = require("fs/promises")
(async () => {
    await fs.rm("directory", { recursive: true })
})()

传统的:

const fs = require("fs")
fs.rm("directory", { recursive: true }, (err) => {
    // Callback
})

force选项也与此处提及有关,因为如果文件夹丢失,它将防止该方法引发错误,这对于清理临时文件很有用。

nodejs 文档

于 2021-05-31T15:33:21.537 回答
2

这是一个适用于 Promise 的异步递归版本。我使用“Q”库,但任何人都会做一些更改(例如“失败”功能)。

为了使用它,我们必须对一些核心 Node 函数进行一些简单的包装,即 fs.stat、fs.readdir、fs.unlink 和 fs.rmdir,以使它们对 Promise 友好。

他们来了:

function getStat(fpath) {
  var def = Q.defer();
  fs.stat(fpath, function(e, stat) {
    if (e) { def.reject(); } else { def.resolve(stat); }
  });
  return def.promise;
}

function readdir(dirpath) {
  var def = Q.defer();
  fs.readdir(dirpath, function(e, files) {
    if (e) { def.reject(e); } else { def.resolve(files); }
  });
  return def.promise;
}

function rmFile(fpath) {
  var def = Q.defer();
  fs.unlink(fpath, function(e) { if(e) { def.reject(e); } else { def.resolve(fpath); }});
  return def.promise;
}

function rmDir(fpath) {
  var def = Q.defer(); 
  fs.rmdir(fpath, function(e) { if(e) { def.reject(e); } else { def.resolve(fpath); }});
  return def.promise;
}

所以这里是递归 rm 函数:

var path = require('path');

function recursiveDelete(fpath) {
  var def = Q.defer();

  getStat(fpath)
  .then(function(stat) {
    if (stat.isDirectory()) {
      return readdir(fpath)
      .then(function(files) {
        if (!files.length) { 
          return rmDir(fpath);
        } else {
          return Q.all(files.map(function(f) { return recursiveDelete(path.join(fpath, f)); }))
          .then(function() { return rmDir(fpath); });
        }
      }); 
    } else {
      return rmFile(fpath);
    }
  })
  .then(function(res) { def.resolve(res); })
  .fail(function(e) { def.reject(e); })
  .done();
  return def.promise;
}
于 2013-10-16T19:32:00.520 回答
1

认为这是深入研究源代码的好借口;)

据我所知,fs.rmdir绑定到 unistd.h 中的 rmdir 函数。从rmdir 的 POSIX 手册页

rmdir() 函数将删除名称由路径给出的目录。仅当目录为空目录时才应删除该目录。

如果目录不是空目录,rmdir() 将失败并将 errno 设置为 [EEXIST] 或 [ENOTEMPTY]。

于 2012-09-27T19:56:52.460 回答
1

除了正确的“否”答案外,rimraf包还提供递归删除功能。它模仿rm -rf. 它也由 Ubuntu正式打包。

于 2013-03-29T18:07:19.973 回答
1

我意识到这并不能完全回答手头的问题,但我认为这可能对将来在这里搜索的人有用(对我来说是这样!):我做了一个小片段,允许递归删除空目录。如果一个目录(或它的任何后代目录)里面有内容,它就会被单独留下:

var fs = require("fs");
var path = require("path");

var rmdir = function(dir) {
    var empty = true, list = fs.readdirSync(dir);
    for(var i = list.length - 1; i >= 0; i--) {
        var filename = path.join(dir, list[i]);
        var stat = fs.statSync(filename);

        if(filename.indexOf('.') > -1) {
            //There are files in the directory - we can't empty it!
            empty = false;
            list.splice(i, 1);
        }
    }

    //Cycle through the list of sub-directories, cleaning each as we go
    for(var i = list.length - 1; i >= 0; i--) {
        filename = path.join(dir, list[i]);
        if (rmdir(filename)) {
            list.splice(i, 1);
        }
    }

    //Check if the directory was truly empty
    if (!list.length && empty) {
        console.log('delete!');
        fs.rmdirSync(dir);
        return true;
    }
    return false;
};

https://gist.github.com/azaslavsky/661020d437fa199e95ab

于 2014-05-13T22:35:39.150 回答
1

我看到的大多数示例都是递归删除节点中的文件夹结构的同步实现。

我还看到了一些实际上效果不佳的异步。

我编写并使用了一个完全异步的:https ://gist.github.com/yoavniran/adbbe12ddf7978e070c0

于 2014-09-23T06:51:40.137 回答
1

同步删除非空目录:-

以下是文件结构 -

在此处输入图像描述

var fs = require('fs');

fs.unlink('./stuff/writeMe.txt',function(){
  fs.rmdirSync('stuff');
})

我首先使用代码从stuff文件夹中删除writeMe.txt文件,这使stuff文件夹为空,最后使用代码将其删除 fs.unlink('./stuff/writeMe.txt')fs.rmdirSync('stuff')

于 2021-01-16T10:31:55.747 回答
0

此函数将递归地同步删除您指定的目录或文件:

var path = require('path');

function deleteRecursiveSync(itemPath) {
    if (fs.statSync(itemPath).isDirectory()) {
        _.each(fs.readdirSync(itemPath), function(childItemName) {
            deleteRecursiveSync(path.join(itemPath, childItemName));
        });
        fs.rmdirSync(itemPath);
    } else {
        fs.unlinkSync(itemPath);
    }
}

如果出现以下情况,我尚未测试此函数的行为:

  • 该项目不存在,或
  • 无法删除该项目(例如由于权限问题)。
于 2013-10-29T15:39:31.963 回答
0

Node.js 的递归删除目录

事实证明,Node.js fs 模块没有递归删除目录及其内容的方法。相反,您应该检查目录结构并删除原子项目,即单个文件和空目录。所以我在https://gist.github.com/2367067找到了一个用 JavaScript 制作的 Takuo Kihira 的好要点,并决定制作一个 CoffeeScript 版本:

于 2014-08-12T02:17:09.587 回答
0

尝试使其失效,因为如果当时正在使用文件或目录,同步删除将导致错误。

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

var dumpDirs = function (dir, name, cb) {
fs.readdir(dir, function (err, files) {
    var dirs = [],
    filePath, i = 0, l = files.length;
    for (var i = 0; i < l; i++) {
        filePath = path.join(dir, files[i]);
        var stats = fs.lstatSync(filePath);
        if (stats.isDirectory()) {
            if (files[i].indexOf(name) != -1) {
                dirs.push({
                    startOn: new Date(stats.ctime),
                    instance: files[i],
                    name: name
                })
            }
        }
    }
    cb(dirs);
});
}

var removeDir = function (dir, callback) {
fs.readdir(dir, function (err, files) {
    c = files.length;

    (function remfile(i, cb) {
        if (i >= c)
            return cb();
        var p = path.join(dir, files[i])
        fs.unlink(p, function (err) {
            if (err) console.log(err);
            remfile(i + 1, cb)
        });

    })(0, function () {
        fs.rmdir(dir, function (err) {
            callback()
        });
    });

    //for (var i = 0; i < c; i++) {
    //    fs.unlinkSync(path.join(dir, files[i]));
    //};


});
}
dumpDirs(maindir, function (dirs) {

if (dirs && dirs.length > 0) {
    (function rem(i, cb) {
        if (i >= dirs.length) {
            return cb();
        }
        var folder = path.join(dump, dirs[i].instance);
        removeDir(folder, function () {
            rem(i + 1, cb);
        });
    })(0, function () {
        callback();
    })
}
else {
    callback();
}
});
于 2014-09-01T13:09:18.743 回答
0

这是我为fluentnode创建的咖啡脚本原型函数,它递归删除文件夹

String::folder_Delete_Recursive = ->
  path = @.toString()
  if path.exists()
    for file in path.files()
      curPath = path.path_Combine(file)
      if curPath.is_Folder()
        curPath.folder_Delete_Recursive()
      else
        curPath.file_Delete()
    fs.rmdirSync(path);

  return path.not_Exists()

这是测试:

it 'folder_Create and folder_Delete' , ->
  tmpDir = "./".temp_Name_In_Folder()
  expect(tmpDir.folder_Exists()).to.be.false
  expect(tmpDir.folder_Create()).to.equal(tmpDir.realPath())
  expect(tmpDir.folder_Exists()).to.be.true
  expect(tmpDir.folder_Delete()).to.be.true
  expect(tmpDir.folder_Exists()).to.be.false

it 'folder_Delete_Recursive' , ->
  tmpDir = "./"   .temp_Name_In_Folder().folder_Create()
  tmpFile = tmpDir.temp_Name_In_Folder().file_Create()
  expect(tmpDir.folder_Delete_Recursive()).to.be.true
于 2014-10-22T08:09:01.790 回答
0

rmdirSync 的简洁同步版本。

/** 
 * use with try ... catch ...
 * 
 * If you have permission to remove all file/dir
 * and no race condition and no IO exception...
 * then this should work 
 *
 * uncomment the line 
 *   if(!fs.exists(p)) return 
 * if you care the inital value of dir, 
 * 
 */
var fs = require('fs')
var path = require('path')

function rmdirSync(dir,file){
  var p = file? path.join(dir,file):dir;
  // if(!fs.exists(p)) return 
  if(fs.lstatSync(p).isDirectory()){
    fs.readdirSync(p).forEach(rmdirSync.bind(null,p))
    fs.rmdirSync(p)
  }
  else fs.unlinkSync(p)
}

还有一个并行 IO,异步版本的 rmdir。(快点)

/**
 * NOTE: 
 * 
 * If there are no error, callback will only be called once.
 * 
 * If there are multiple errors, callback will be called 
 * exactly as many time as errors occur. 
 * 
 * Sometimes, this behavior maybe useful, but users 
 * should be aware of this and handle errors in callback. 
 * 
 */

var fs = require('fs')
var path = require('path')

function rmfile(dir, file, callback){
  var p = path.join(dir, file)
  fs.lstat(p, function(err, stat){
    if(err) callback.call(null,err)
    else if(stat.isDirectory()) rmdir(p, callback)
    else fs.unlink(p, callback)
  })
}

function rmdir(dir, callback){
  fs.readdir(dir, function(err,files){
    if(err) callback.call(null,err)
    else if( files.length ){
      var i,j
      for(i=j=files.length; i--; ){
        rmfile(dir,files[i], function(err){
          if(err) callback.call(null, err)
          else if(--j === 0 ) fs.rmdir(dir,callback)
        })
      }
    }
    else fs.rmdir(dir, callback)
  })
}

无论如何,如果您想要一个顺序 IO,并且只调用一次回调(成功或遇到第一个错误)。用上面的替换这个 rmdir。(慢点)

function rmdir(dir, callback){
  fs.readdir(dir, function(err,files){
    if(err) callback.call(null,err)
    else if( files.length ) rmfile(dir, files[0], function(err){
      if(err) callback.call(null,err)
      else rmdir(dir, callback)
    })
    else fs.rmdir(dir, callback)
  })
}

它们都只依赖于node.js并且应该是可移植的。

于 2014-11-06T12:56:33.547 回答
0

这篇文章得到了谷歌的最佳答案,但没有一个答案给出了解决方案:

  • 不使用同步功能

  • 不需要外部库

  • 不直接使用 bash

这是我的async解决方案,它不假设安装节点以外的任何其他内容:

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

function rm(path){  
    return stat(path).then((_stat) => {                                   

    if(_stat.isDirectory()){                                                                                                                                                                                                                          
      return ls(path)                                                                                                                                                                                                                                   
        .then((files) => Promise.all(files.map(file => rm(Path.join(path, file)))))
       .then(() => removeEmptyFolder(path));                                                                                                                                                                                                 
    }else{                                                                                                                                                                                                                                            
      return removeFileOrLink(path);                                                                                                                                                                                                            
    }   });
                                                                                                                                                                                                                                              function removeEmptyFolder(path){                                     

    return new Promise((done, err) => {                                                                                                                                                                                                               
      fs.rmdir(path, function(error){                                                                                                                                                                                                                   
        if(error){ return err(error); }                                                                                                                                                                                                               
        return done("ok");                                                                                                                                                                                                                        
      });                                                                                                                                                                                                                                       
    });                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                function removeFileOrLink(path){                                      

    return new Promise((done, err) => {                                                                                                                                                                                                               
      fs.unlink(path, function(error){                                                                                                                                                                                                                  
        if(error){ return err(error); }                                                                                                                                                                                                               
        return done("ok");                                                                                                                                                                                                                        
      });                                                                                                                                                                                                                                       
    });                                                                                                                                                                                                                                          }

                                                                                                                                                                                                                                                function ls(path){                                                    

    return new Promise((done, err) => {                                                                                                                                                                                                               
      fs.readdir(path, function (error, files) {                                                                                                                                                                                                        
        if(error) return err(error)                                                                                                                                                                                                                   
        return done(files)                                                                                                                                                                                                                        
      });                                                                                                                                                                                                                                       
    });                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                function stat(path){                                                  

    return new Promise((done, err) => {                                                                                                                                                                                                               
      fs.stat(path, function (error, _stat) {                                                                                                                                                                                                           
        if(error){ return err(error); }                                                                                                                                                                                                               
        return done(_stat);                                                                                                                                                                                                                       
      });                                                                                                                                                                                                                                       
    });                                                                                                                                                                                                                                          } }
于 2017-06-26T14:12:17.257 回答
0

遵循@geedew 的回答。

这是一个异步实现rm -r(即您可以将路径传递给文件或目录)。我不是经验丰富的 nodejs 开发人员,我感谢任何建议或建设性的批评。

var fs = require('fs');

function ResultsCollector (numResultsExpected, runWhenDone) {
    this.numResultsExpected = numResultsExpected,
    this.runWhenDone = runWhenDone;
    this.numResults = 0;
    this.errors = [];

    this.report = function (err) {
        if (err) this.errors.push(err);
        this.numResults++;
        if (this.numResults == this.numResultsExpected) {
            if (this.errors.length > 0) return runWhenDone(this.errors);
            else return runWhenDone();
        }
    };
}

function rmRasync(path, cb) {
    fs.lstat(path, function(err, stats) {
        if (err && err.code == 'ENOENT') return cb(); // doesn't exist, nothing to do
        else if (err) {
            return cb(err);
        }
        if (stats.isDirectory()) {
            fs.readdir(path, function (err, files) {
                if (err) return cb(err);
                var resultsCollector = new ResultsCollector(files.length, function (err) {
                    if (err) return cb(err);
                    fs.rmdir(path, function (err) {
                        if (err) return cb(err);
                        return cb();
                    });
                });
                files.forEach(function (file) {
                    var filePath = path + '/' + file;
                    return rmRasync(filePath, function (err) {
                        return resultsCollector.report(err);
                    });
                });
            });
        }
        else { // file.
            // delete file or link
            fs.unlink(path, function (err) {
                if (err) return cb(err);
                return cb();
            });
        }
    });
};

像这样调用:

rmRasync('/path/to/some/file/or/dir', function (err) {
    if (err) return console.error('Could not rm', err);
    // else success
});
于 2018-06-05T08:00:21.097 回答
0

这里令人惊讶的冗长和糟糕的答案......

要在大多数系统上删除非空目录:

import * as cp from 'child_process';

const dir = '/the/dir/to/remove';

const k = cp.spawn('bash');

k.stdin.end(`rm -rf "${dir}"`);

k.once('exit', code => {
   // check the exit code
   // now you are done
});

这适用于 MacOS 和 Linux,但可能不适用于某些 Windows 操作系统。

于 2018-07-13T03:41:42.017 回答