84

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 ?

Samuel Rossille
  • 18,940
  • 18
  • 62
  • 90
  • 11
    What happened when you tried it? – beny23 Sep 27 '12 at 18:13
  • 12
    @beny23 See my answer ;=). This is just a question / answer couple to share knowlegde, so next time sombody googles "nodejs rmdir non-empty directory", he finds the answer. You can use SO for this when you have to investigate on a question which answer could interest other people – Samuel Rossille Sep 27 '12 at 18:42
  • This is just one example of a generic issue. Node.js leans heavily on the posix layer underneath (and it's not unique in that). So this question should not become a precedent for people asking about similar unix functions wrapped in *node.js* (or ruby, or ...). So can we improve the question into something more generic, while keeping rmdir as a specific example? – Henk Langeveld Sep 28 '12 at 12:25
  • Does this answer your question? [Remove directory which is not empty](https://stackoverflow.com/questions/18052762/remove-directory-which-is-not-empty) – KyleMit Jan 03 '20 at 19:25

23 Answers23

95

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 install it and save to 'package.json' (other save options can be found in the npm-install docs)

npm install --save rimraf

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)->
Jeremy
  • 3,418
  • 2
  • 32
  • 42
Besi
  • 22,579
  • 24
  • 131
  • 223
  • 7
    there's no need for `require('./node_modules/rimraf');` - you could just `require('rimraf');` – surui Sep 28 '13 at 13:18
  • 1
    When I run this as part of a cakefile on Windows (under Git Bash) the error callback is called but with a null argument value, however it does seem to correctly delete the specified directories. Is this intentional? – Dai Jul 12 '16 at 05:15
  • 2
    rimraf is [ported](https://github.com/nodejs/node/pull/29168) to Node.js core and `fs.rmdir` supports `recursive` option since Node.js v12.10.0. See [my answer](https://stackoverflow.com/a/57805342/3039956). – kimamula Sep 07 '19 at 02:09
60

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.

geedew
  • 1,368
  • 12
  • 16
  • 2
    This is not a good idea. There exists `fs.rmdir`, and it won't work on Windows, too. – nalply Oct 06 '12 at 21:24
  • 8
    This is a bad idea: you loose portability, and besides, hacking with native executable is always more dangerous and sensitive than using the APIs of your language. – Samuel Rossille Oct 07 '12 at 01:16
  • 1
    I added another solution that shows a synchronous way of accomplishing this task. – geedew Oct 25 '12 at 03:58
  • Symbolink links inside the path throw an `Error: ENOTDIR, not a directory` error. – Jürgen Paul Jul 15 '13 at 02:58
  • 1
    @PineappleUndertheSea - That's because the example used `stat()` when it should have used `lstat()`, which also means that it was following the link and deleting the target directory's contents before it errored out! Eek! I proposed an edit to change it to `lstat`. – Kenton Varda Feb 21 '14 at 09:37
  • @KentonVarda Thanks, good catch. I'm not sure `lstat()` was around when written before. Updated the post though. – geedew Mar 01 '14 at 05:57
  • 1
    Some files may not be deleteable so adding chmodSync(path, 511) before deleting files may help. – TOAOGG Jul 29 '14 at 16:23
  • 2
    Use `path.join(path, file)` instead of `path + "/" + file` – Ludohen Oct 07 '15 at 11:34
38

Short answer: node.js fs.rmdir() calls the POSIX rmdir(); this will remove an empty directory, or return an error. In the given case, the call will invoke the callback function and pass the error as an exception.

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

The Node.js API Docs File System API started out as

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.

Henk Langeveld
  • 8,088
  • 1
  • 43
  • 57
23

Node.js v12.10.0 introduced recursive option into fs.rmdir. As fs.mkdir supports the same option since v10.12.0, both making and removing directory can be executed recursively.

$ 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
kimamula
  • 11,427
  • 8
  • 35
  • 29
  • I've confirmed by checking the releases notes for v12.10.0. https://nodejs.org/en/blog/release/v12.10.0/ – Edmond Weiss Sep 20 '19 at 10:47
  • Prior to v12.10.0, the selected answer was the best option. However, @kimamula is correct, anyone running on the LTS or Current version of Node.js should be looking toward this answer. (Recasting my vote here a few years later!) – isaacdre Mar 30 '20 at 19:49
  • **deprecated** - see https://stackoverflow.com/a/67776645/601466 below or https://nodejs.org/api/deprecations.html#DEP0147 – borisdiakur Nov 23 '21 at 10:30
17

This worked for me

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

Edit 2021:

Now it seems to have been replaced in v14 with:

fs.rmSync('./output', {recursive: true, force: true});
OOM
  • 728
  • 7
  • 22
  • I don't know if it's me or a general problem, but files inside deleted directory will act very odd. after `rmdirSync` is done, all the files are inaccessible due to administrative privilege! – Rafe Jul 11 '21 at 07:56
  • 1
    Hello @Rafe, I'm not sure how you can still access your files after deletion. Under which OS are you? Try to refresh the folder. – OOM Jul 11 '21 at 10:12
  • 3
    Finally found it out. I was using winston logger and somehow will leave the log file open, and will cause this odd problem. Thanks for your rapid reply ;) – Rafe Jul 11 '21 at 10:24
7

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.

reallynice
  • 1,289
  • 2
  • 21
  • 41
6

As of node v16, recursive fs.rmdir is now deprecated. The replacement is fs.rm.

Usage with promises:

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

Traditional:

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

The force option is also relevant to mention here as it will prevent the method from throwing errors if the folder is missing, which is useful if this is for cleaning up temporary files.

nodejs documentation

Daniel Vestøl
  • 480
  • 7
  • 16
5

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.

Thorsten Lorenz
  • 11,781
  • 8
  • 52
  • 62
3

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

zupa
  • 12,809
  • 5
  • 40
  • 39
2

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;
}
kliron
  • 4,383
  • 4
  • 31
  • 47
  • I wrote a similar solution also using Q :) I like yours too. Is the files.length check needed? In my solution I'm not checking it and it is deleting the folder properly. You can find my code her: https://gist.github.com/Sharian/067d1f572d1a00655e5a – TOAOGG Mar 11 '15 at 15:32
  • why not `Q.ninvoke(fs, "rmdir"...)`? – Imskull Aug 11 '15 at 13:10
1

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].

mike
  • 7,137
  • 2
  • 23
  • 27
1

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.

gustaf r
  • 1,224
  • 9
  • 14
1

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

AlexZ
  • 11,515
  • 3
  • 28
  • 42
1

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

poeticGeek
  • 1,001
  • 10
  • 14
1

Removing NON-EMPTY directories SYNCHRONOUSLY:-

Following is the file structure -

enter image description here

var fs = require('fs');

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

I am firstly removing the writeMe.txt file from stuff folder using code fs.unlink('./stuff/writeMe.txt') which makes the stuff folder empty and finally removing it using code fs.rmdirSync('stuff')

Suprabhat Kumar
  • 645
  • 7
  • 13
0

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).
David Foster
  • 6,931
  • 4
  • 41
  • 42
0

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:

sourcecode
  • 4,116
  • 1
  • 20
  • 14
0

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

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
Dinis Cruz
  • 4,161
  • 2
  • 31
  • 49
0

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.

Jack Tang
  • 486
  • 6
  • 7
0

This post was getting the top answer from google but none of the answers gives a solution that:

  • doesn't make use of sync functions

  • doesn't require external libraries

  • doesn't use bash directly

Here is my async solution which doesn't assume anything else than node installed:

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);                                                                                                                                                                                                                       
      });                                                                                                                                                                                                                                       
    });                                                                                                                                                                                                                                          } }
JP. Aulet
  • 4,375
  • 4
  • 26
  • 39
0

Following on @geedew's answer.

Here is an asynchronous implementation of rm -r (ie you can pass a path to a file or directory). I'm not an experienced nodejs developer and appreciate any suggestions or constructive criticism.

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

Invoke like so:

rmRasync('/path/to/some/file/or/dir', function (err) {
    if (err) return console.error('Could not rm', err);
    // else success
});
mattpr
  • 2,504
  • 19
  • 17
0

Surprisingly verbose and bad answers here...

To delete a non-empty directory on most systems:

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

this will work on MacOS and Linux, but it might not work on some Windows OS.

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • Please don't do this - you are opening your app up to security issues if you pass user input through this code. Use a tested library like `rimraf` instead. – We Are All Monica Feb 25 '19 at 02:17
  • rimraf accepts strings too dude, its no more secure than this – Alexander Mills Feb 25 '19 at 02:23
  • The specific problem is that it is very easy to break out of the quoting you've set up and execute arbitrary shell commands. `rimraf` does not have this problem because it doesn't use the shell. Also, in general, a well-maintained library will be updated if any further issues arise, and one-off code like this will not. – We Are All Monica Feb 25 '19 at 19:29
  • @jnylen this is backend code - it's can't easily be changed by a bad actor – Alexander Mills Feb 25 '19 at 20:37
  • If this code accepts user input in the `dir` variable then your server is ready to be compromised. If someone else copies this code into a place where it accepts user input in the `dir` variable, then their server is ready to be compromised. – We Are All Monica Feb 25 '19 at 20:38
  • sure, that's why youre supposed to sanitize your inputs – Alexander Mills Feb 26 '19 at 00:00