Is node.js rmdir recursive ? Will it work on non empty directories?

The documentation for fs.rmdir is very short and doesn't explain the behavior of rmdir when the directory is not empty.

Q: What happens if I try to use this API to delete a non empty directory ?

Although using a third-party library for such a thing I could not come up with a more elegant solution. So I ended up using the npm-module rimraf.

Install it

npm install rimraf

Or add it to your Package.json

"devDependencies": {
    "rimraf": "2.1",
},

Then you can do the following:

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

Or in Coffeescript:

rmdir = require 'rimraf'
rmdir 'some/directory/with/files', (error)->

I wrote about this problem exactly.

My previous solution below, while simple, is not preferred. The following function, is a Synchronous solution; while async might be preferred.

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

[Edit] Added lstat instead of stat to prevent errors on symlinks

[Previous Solution]

My solution to this is quite easy to implement.

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

This is slimmed down for this page, but the basic idea is simple; execute 'rm -r' on the command line. If your app needs to run across different types of OS, put this in a function and have an if/else/switch to handle it.

You will want to handle all the responses; but the idea is simple enough.

Short answer: node.js fs.rmdir() calls the POSIX rmdir(); this will remove an empty directory, or return an error.

The problem here is that the node.js documentation refers to POSIX:

The Node.js API Docs File System intro says:

File I/O is provided by simple wrappers around standard POSIX functions.

This almost changes the question into a duplicate of: Is there a listing of the POSIX API / functions?

The description for fs.rmdir is terse, but sufficient.

Asynchronous rmdir(2).

The rmdir(2) here is an implicit reference to the documentation for the rmdir() system call. The number (2) here is an old unix man page convention to indicate Section 2 of the Manual pages, containing the kernel interfaces.

Just a small dot among this bunch of answers, but I think it's good to point it out.

Personally (and generally) I would prefer to use an already existing library, if there's one available, for doing the task. Taking an already existing thing means, for me and especially in the open source world, using and improving an already existing thing, which could end up in a better result than doing it on my own (I'm improving something that some one other has done).

In this case, with a small search I found out the module fs-extra, which aims to be a replacement also for rimraf and answers to the need to remove recursively directories (apparently with async and sync versions). Furthermore, it has got a good number of stars on github and seems currently mantained: these two conditions, in addition to the fact that answers to the need, make it the way to go (almosto for a bit) for me.

fs.rmdir is not recursive.

You could instead use a recursive fs.readdir module like readdirp in order to find all files and directories . Then remove all files, followed by all directories.

For an even simpler solution have a look at rimraf.

Here is an asynchronous recursive version that works with promises. I use the 'Q' library but anyone will do with a few changes (eg the 'fail' function).

To make use of it, we must make a few simple wrappers around some core Node functions, namely fs.stat, fs.readdir, fs.unlink and fs.rmdir to make them promise-friendly.

Here they are:

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

So here is the recursive rm function:

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

Use child_process.execFile it is faster.

NodeJS docs:

child_process.execFile is similar to child_process.exec() except it* does not execute a subshell but rather the specified file directly.

This works. Mimicking 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']);

Edit: I have to admit this is not cross-platform, will not work on Windows

In addition to the correct "no" answers, the rimraf package provides recursive delete functionality. It mimics rm -rf. It's also officially packaged by Ubuntu.

I realize this isn't exactly answering the question at hand, but I think this might be useful to someone searching here in the future (it would have been to me!): I made a little snippet that allows one to recursively delete only empty directories. If a directory (or any of its descendant directories) has content inside it, it is left alone:

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

Recursive remove directory for Node.js

It turned out that Node.js fs module does not have a method to remove the directory and its content recursively. Instead you should go through the directory structure and remove atomic items, i.e. individual files and empty directories. So I found a nice gist by Takuo Kihira at https://gist.github.com/2367067 made in JavaScript and decided to make a CoffeeScript version of it:

Figured this was a good excuse to take a dive into the source ;)

From what I can tell, fs.rmdir is bound to the rmdir function from unistd.h. From the POSIX man page for rmdir:

The rmdir() function shall remove a directory whose name is given by path. The directory shall be removed only if it is an empty directory.

If the directory is not an empty directory, rmdir() shall fail and set errno to [EEXIST] or [ENOTEMPTY].

most of the examples I see out there are synchronous implementations of recursively deleting a folder structure in node.

I've also seen a few asynchronous ones that dont actually work well.

I wrote and use one thats completely asynchronous: https://gist.github.com/yoavniran/adbbe12ddf7978e070c0

This is my ECMAscript6 version for recursive delete using promises and it works in the current release of nodejs

FileSystemAdapter.deleteDirectoryES6 = function(path)
{
    return new Promise(function(resolve, reject)
    {
        fs.readdir(path, function(err, files)
        {
            if(!err)
            {
                var ContentRemoved = [];
                files.forEach(function(element)
                {
                    ContentRemoved.push(FileSystemAdapter.remove(path+'\\'+element));
                });
                Promise.all(ContentRemoved)
                    .then(function()
                    {
                        fs.rmdir(path, function(err)
                        {
                            if(!err)  resolve();
                            else      reject(err);
                        });
                    });
            }
            else reject(err);

        });
    });
};

FileSystemAdapter.deleteFileES6 = function(path)
{
    return new Promise(function(resolve, reject)
    {
        fs.unlink(path, function(err)
        {
            if(!err) resolve();
            else reject(err);
        });
    });
};

FileSystemAdapter.removeES6 = function(path)
{
    return new Promise(function(resolve, reject)
    {
        fs.lstat(path, function(err, stats)
        {
            var ContentPromise = stats.isDirectory() ? FileSystemAdapter.deleteDirectoryES6(path) : FileSystemAdapter.deleteFileES6(path);
            ContentPromise.then(function() { resolve(); }).catch(function(error) { reject(error) });
        });
    });
};

tried to make it fail safe as sync removal will cause error if file or dir are in use at that time.

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

Here is the coffee script prototype function I created for fluentnode that deletes a folder recursively

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

here is the test:

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

A neat synchronous version of 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)
}

And a parallel IO, asynchronous version of rmdir. (faster)

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

Anyway, if you want a sequential IO, and callback be called exactly once (either success or with first error encountered). Replace this rmdir with the above. (slower)

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

All of them depend ONLY on node.js and should be portable.

This function will recursively delete a directory or file that you specify, synchronously:

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

I have not tested this function's behavior if:

  • the item does not exist, or
  • the item cannot be deleted (such as due to a permissions issue).