37

I want to delete several files from a directory, matching a regex. Something like this:

// WARNING: not real code
require('fs').unlink(/script\.\d+\.js$/);

Since unlink doesn't support regexes, I'm using this instead:

var fs = require('fs');

fs.readdir('.', (error, files) => {
    if (error) throw error;

    files.filter(name => /script\.\d+\.js$/.test(name)).forEach(fs.unlink);
});

which works, but IMO is a little more complex than it should be.


Is there a better built-in way to delete files that match a regex (or even just use wildcards)?

Joseph Silber
  • 214,931
  • 59
  • 362
  • 292
  • 2
    Seems reasonable. What makes it seem overly complex? – loganfsmyth Feb 17 '13 at 03:50
  • 2
    @loganfsmyth - Because I *want* it to be simpler? :) – Joseph Silber Feb 17 '13 at 03:51
  • I suspect the downvote is because you're basically asking a question and answering it yourself. Personally, I think your code is nice and rather concise. Too much more concise and it might actually start obscuring the functionality. I'd just modularize it, passing the regex and the directory name as arguments. An upvote for you, sir! – Will Nelson Feb 17 '13 at 04:30
  • 1
    @WillNelson - I'm not asking for anybody to refactor my code (I would've posted to [codereview](http://codereview.stackexchange.com) for that). I'm asking if there's a better *built-in* way to accomplish this. – Joseph Silber Feb 17 '13 at 04:34

4 Answers4

38

No there is no globbing in the Node libraries. If you don't want to pull in something from NPM then not to worry, it just takes a line of code. But in my testing the code provided in other answers mostly won't work. So here is my code fragment, tested, working, pure native Node and JS.

let fs = require('fs')
const path = './somedirectory/'
let regex = /[.]txt$/
fs.readdirSync(path)
    .filter(f => regex.test(f))
    .map(f => fs.unlinkSync(path + f))
david.pfx
  • 10,520
  • 3
  • 30
  • 63
  • 2
    this should be the best answer. copy, paste, tune regex, and files I want to delete is deleted. – Даниил Пронин Jan 27 '20 at 01:49
  • 1
    you have `path` and `dpath`, is that supposed to be different? – João Pimentel Ferreira Mar 15 '20 at 22:21
  • 1
    @JoãoPimentelFerreira: oops, sorry, fixed. – david.pfx Mar 17 '20 at 03:16
  • 1
    I think `forEach` is more appropriate than `map` in this example – zestyrx Jul 14 '21 at 23:19
  • @mracette: Why? Feel free to provide your own answer and explain why it's better. I don't see it. – david.pfx Jul 16 '21 at 00:29
  • @david.pfx they are functionally equivalent, and this is a great answer, but `forEach` is more idiomatic because it does not collect the return values of `fs.unlinkSync` and put them into a new array, which is unnecessary overhead since the new array is not used. I think this thread covers the issue well: https://stackoverflow.com/questions/34426458/javascript-difference-between-foreach-and-map – zestyrx Jul 17 '21 at 13:57
  • Can you explain how to create the regex if it includes variables? – regan Sep 02 '21 at 02:38
  • @regan: that looks like a regex question. Try this https://stackoverflow.com/questions/494035/how-do-you-use-a-variable-in-a-regular-expression or ask a new question. – david.pfx Sep 04 '21 at 01:15
12

You can look into glob https://npmjs.org/package/glob

require("glob").glob("*.txt", function (er, files) { ... });
//or
files = require("glob").globSync("*.txt");

glob internally uses minimatch. It works by converting glob expressions into JavaScript RegExp objects. https://github.com/isaacs/minimatch

You can do whatever you want with the matched files in the callback (or in case of globSync the returned object).

user568109
  • 47,225
  • 17
  • 99
  • 123
  • +1 Thank you, but I'm looking for a built-in solution; I don't think this justifies introducing another dependency to my project. If there's no better native solution, I think the code posted will serve me just fine. – Joseph Silber Feb 17 '13 at 18:28
  • 1
    as a follow up (albeit a few months late), wanting to do things using "built in functions" in Node.js is not quite the Node.js way. It is bundled with `npm` specifically because it is incredibly easy to add a new library using `npm install glob --save` (in this case). For Node.js development, "through npm" and "built in" are virtually the same thing. – Mike 'Pomax' Kamermans Sep 13 '13 at 22:59
1

I have a very simple solution to do this. Read the directory in node.js using fs.readdir API. This will give an array of all the files in the directory. Once you have that array, iterate over it using for loop, apply regex. The below code will delete all files starting with "en" and extension ".js"

fs.readdir('.', (err, files)=>{
   for (var i = 0, len = files.length; i < len; i++) {
      var match = files[i].match(/en.*.js/);
      if(match !== null)
          fs.unlink(match[0]);
   }
});
Sumit Jindal
  • 363
  • 1
  • 7
  • 17
0

The answer could depend on your environment. It looks like you are running on node.js. A quick perusal of the node.js documentation suggests there is no "built in" way to do this, i.e., there isn't a single function call that will do this for you. The next best thing might involve a small number of function calls. As I wrote in my comment, I don't think there's any easy way to make your suggested answer much briefer just relying on the standard node.js function calls. That is, if I were in your shoes, I would go with the solution you already suggested (though slightly cleaned up).

One alternative is to go to the shell, e.g.,

var exec = require('child_process').exec;
exec('ls | grep "script[[:digit:]]\\\+.js" | xargs rm');

Personally, I would strongly prefer your offered solution over this gobbledygook, but maybe you're shooting for something different.

Will Nelson
  • 966
  • 8
  • 13