在nodejs的文件夹下按扩展名* .html查找文件


89

我想使用nodejs在src文件夹及其所有子文件夹中找到所有* .html文件。最好的方法是什么?

var folder = '/project1/src';
var extension = 'html';
var cb = function(err, results) {
   // results is an array of the files with path relative to the folder
   console.log(results);

}
// This function is what I am looking for. It has to recursively traverse all sub folders. 
findFiles(folder, extension, cb);

我认为很多开发人员都应该拥有出色且经过测试的解决方案,使用它比自己编写一个更好。


如果要按正则表达式搜索文件,请使用file-regex库,该库同时进行递归文件搜索。
Akash Babu

Answers:


90

node.js,递归简单函数:

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

function fromDir(startPath,filter){

    //console.log('Starting from dir '+startPath+'/');

    if (!fs.existsSync(startPath)){
        console.log("no dir ",startPath);
        return;
    }

    var files=fs.readdirSync(startPath);
    for(var i=0;i<files.length;i++){
        var filename=path.join(startPath,files[i]);
        var stat = fs.lstatSync(filename);
        if (stat.isDirectory()){
            fromDir(filename,filter); //recurse
        }
        else if (filename.indexOf(filter)>=0) {
            console.log('-- found: ',filename);
        };
    };
};

fromDir('../LiteScript','.html');

如果想花哨的话,添加RegExp,并添加回调使其通用。

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

function fromDir(startPath,filter,callback){

    //console.log('Starting from dir '+startPath+'/');

    if (!fs.existsSync(startPath)){
        console.log("no dir ",startPath);
        return;
    }

    var files=fs.readdirSync(startPath);
    for(var i=0;i<files.length;i++){
        var filename=path.join(startPath,files[i]);
        var stat = fs.lstatSync(filename);
        if (stat.isDirectory()){
            fromDir(filename,filter,callback); //recurse
        }
        else if (filter.test(filename)) callback(filename);
    };
};

fromDir('../LiteScript',/\.html$/,function(filename){
    console.log('-- found: ',filename);
});

非常感谢您的演示代码!我在您的代码之上添加了一些东西,它很好用!我还检查了您的LiteScript项目,这非常了不起。我已经在github上加注了星标!
Nicolas S.Xu 2014年

查找没有扩展名的文件名的漂亮小脚本-在我的情况下,我有一些Jpegs,需要查找其他目录中的原始文件是png还是jpeg,这很有帮助
Ricky Odin Matthews

78

我喜欢使用glob包:

const glob = require('glob');

glob(__dirname + '/**/*.html', {}, (err, files)=>{
  console.log(files)
})

1
通常,不喜欢简单事物的软件包,但是glob具有内置的node js实现只是时间问题。这已成为文件选择的正则表达式。
Seph Reed

27

什么,等等?...好吧,也许这对其他人也更有意义。

[ nodejs 7请注意]

fs = import('fs');
let dirCont = fs.readdirSync( dir );
let files = dirCont.filter( function( elm ) {return elm.match(/.*\.(htm?html)/ig);});

使用regex做任何事情,使其成为您在函数中使用默认值等设置的参数。


2
这只会在根目录中获得匹配的文件。
dreamerkumar

6
我尝试编辑但被拒绝,对此我表示不同意。这是我的建议:stackoverflow.com/review/suggested-edits/19188733 wl非常有意义。此外,缺少fs的导入。您需要的三行内容是:1. const fs = require('fs');2. const dirCont = fs.readdirSync( dir );3.const files = dirCont.filter( ( elm ) => /.*\.(htm?html)/gi.test(elm) );
Avindra Goolcharan

抱歉,wl.fs是我通过导入存储fs库的地方。
大师詹姆斯

哦,导入可能是我自己的自定义函数,现在也指向了require,因此请确保使用require或您必须执行的任何操作。
大师詹姆斯

13

根据Lucio的代码,我制作了一个模块。它将返回所有带有特定扩展名的文件。如果有人需要,请将其张贴在这里。

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


/**
 * Find all files recursively in specific folder with specific extension, e.g:
 * findFilesInDir('./project/src', '.html') ==> ['./project/src/a.html','./project/src/build/index.html']
 * @param  {String} startPath    Path relative to this file or other file which requires this files
 * @param  {String} filter       Extension name, e.g: '.html'
 * @return {Array}               Result files with path string in an array
 */
function findFilesInDir(startPath,filter){

    var results = [];

    if (!fs.existsSync(startPath)){
        console.log("no dir ",startPath);
        return;
    }

    var files=fs.readdirSync(startPath);
    for(var i=0;i<files.length;i++){
        var filename=path.join(startPath,files[i]);
        var stat = fs.lstatSync(filename);
        if (stat.isDirectory()){
            results = results.concat(findFilesInDir(filename,filter)); //recurse
        }
        else if (filename.indexOf(filter)>=0) {
            console.log('-- found: ',filename);
            results.push(filename);
        }
    }
    return results;
}

module.exports = findFilesInDir;

12

您可以使用Filehound执行此操作。

例如:在/ tmp中找到所有.html文件:

const Filehound = require('filehound');

Filehound.create()
  .ext('html')
  .paths("/tmp")
  .find((err, htmlFiles) => {
    if (err) return console.error("handle err", err);

    console.log(htmlFiles);
});

有关更多信息(和示例),请查看文档:https : //github.com/nspragg/filehound

免责声明:我是作者。


8

我查看了以上答案,并将这个适用于我的版本混合在一起:

function getFilesFromPath(path, extension) {
    let files = fs.readdirSync( path );
    return files.filter( file => file.match(new RegExp(`.*\.(${extension})`, 'ig')));
}

console.log(getFilesFromPath("./testdata", ".txt"));

此测试将从路径下的文件夹中找到的文件返回文件名数组./testdata。正在处理节点版本8.11.3。


1
我会在RegExp的末尾添加$:.*\.(${extension})$
Eugene

3

您可以使用操作系统帮助。这是一个跨平台的解决方案:

1.波纹管功能使用lsdir不进行递归搜索,但是它具有相对路径

var exec = require('child_process').exec;
function findFiles(folder,extension,cb){
    var command = "";
    if(/^win/.test(process.platform)){
        command = "dir /B "+folder+"\\*."+extension;
    }else{
        command = "ls -1 "+folder+"/*."+extension;
    }
    exec(command,function(err,stdout,stderr){
        if(err)
            return cb(err,null);
        //get rid of \r from windows
        stdout = stdout.replace(/\r/g,"");
        var files = stdout.split("\n");
        //remove last entry because it is empty
        files.splice(-1,1);
        cb(err,files);
    });
}

findFiles("folderName","html",function(err,files){
    console.log("files:",files);
})

2.波纹管功能使用finddir进行递归搜索,但是在Windows上它具有绝对路径

var exec = require('child_process').exec;
function findFiles(folder,extension,cb){
    var command = "";
    if(/^win/.test(process.platform)){
        command = "dir /B /s "+folder+"\\*."+extension;
    }else{
        command = 'find '+folder+' -name "*.'+extension+'"'
    }
    exec(command,function(err,stdout,stderr){
        if(err)
            return cb(err,null);
        //get rid of \r from windows
        stdout = stdout.replace(/\r/g,"");
        var files = stdout.split("\n");
        //remove last entry because it is empty
        files.splice(-1,1);
        cb(err,files);
    });
}

findFiles("folder","html",function(err,files){
    console.log("files:",files);
})

1
我从没想过可以这样做,因为我不熟悉require('child_process')。exec,但是它看起来非常好,并且激发了我很多想法。谢谢!
Nicolas S.Xu 2014年

2
这不是“使用nodejs”实现的方法。这使用的是操作系统,正在启动另一个进程,等等。如果目录以“ .html”结尾,也将失败,例如:files.html /
Lucio M. Tato

@ LucioM.Tato您可以在搜索时指定文件类型。解决问题的方法很多,如果一个问题与您的想法不符,那并不意味着它是错的,而是不同的。该答案证明,无论使用哪种脚本语言,您都可以重用现有的解决方案。
埃米尔·康德里亚

当然,遍历目录并查找具有特定扩展名的文件并没有错,但是我只是想从OS接收所有这些信息,因为我知道他可以做到。:)
Emil Condrea 2014年

@ EmilCondrea,IHMO,这不是OP所要求的“使用节点”。无论如何,如果这困扰着我,我将删除下注。
Lucio M. Tato 2014年

3

以下代码在./内进行递归搜索(适当更改),并返回以.html结尾的绝对文件名数组

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

var searchRecursive = function(dir, pattern) {
  // This is where we store pattern matches of all files inside the directory
  var results = [];

  // Read contents of directory
  fs.readdirSync(dir).forEach(function (dirInner) {
    // Obtain absolute path
    dirInner = path.resolve(dir, dirInner);

    // Get stats to determine if path is a directory or a file
    var stat = fs.statSync(dirInner);

    // If path is a directory, scan it and combine results
    if (stat.isDirectory()) {
      results = results.concat(searchRecursive(dirInner, pattern));
    }

    // If path is a file and ends with pattern then push it onto results
    if (stat.isFile() && dirInner.endsWith(pattern)) {
      results.push(dirInner);
    }
  });

  return results;
};

var files = searchRecursive('./', '.html'); // replace dir and pattern
                                                // as you seem fit

console.log(files);

2

由于声誉而无法添加评论,但请注意以下几点:

使用fs.readdir或node-glob在500,000个文件的文件夹中查找通配符文件集大约需要2秒钟。与DIR一起使用exec花费了〜0.05s(非递归)或〜0.45s(递归)。(我正在单个目录中查找〜14个与我的模式匹配的文件)。

到目前为止,我还没有找到任何使用低级OS通配符搜索效率的nodejs实现。但是,就效率而言,以上基于DIR / ls的代码在Windows中效果很好。但是,对于大型目录,linux find可能会非常慢


确实很有趣。
philk

注意,我看到最新的nodejs fs模块(12.13+?迭代目录fns?)中有新功能。我还没有尝试过,因为我现在停留在6.9.11上;看看他们是否为此提供了任何新的有用功能,将非常有趣。现在想我的帖子;还应考虑操作系统缓存。运行多次后,很可能会测量我的0.05s。我想知道第一个“ DIR”速度是多少?
西蒙H

1

我的两便士,使用地图代替for循环

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

var findFiles = function(folder, pattern = /.*/, callback) {
  var flist = [];

  fs.readdirSync(folder).map(function(e){ 
    var fname = path.join(folder, e);
    var fstat = fs.lstatSync(fname);
    if (fstat.isDirectory()) {
      // don't want to produce a new array with concat
      Array.prototype.push.apply(flist, findFiles(fname, pattern, callback)); 
    } else {
      if (pattern.test(fname)) {
        flist.push(fname);
        if (callback) {
          callback(fname);
        }
      }
    }
  });
  return flist;
};

// HTML files   
var html_files = findFiles(myPath, /\.html$/, function(o) { console.log('look what we have found : ' + o} );

// All files
var all_files = findFiles(myPath);


0

我刚刚注意到,您正在使用sync fs方法,这可能会阻塞您的应用程序,这是一种使用asyncq的基于承诺的异步方法,您可以使用START = / myfolder FILTER =“。jpg”节点myfile.js执行它,假设您将以下代码放在名为myfile.js的文件中:

Q = require("q")
async = require("async")
path = require("path")
fs = require("fs")

function findFiles(startPath, filter, files){
    var deferred;
    deferred = Q.defer(); //main deferred

    //read directory
    Q.nfcall(fs.readdir, startPath).then(function(list) {
        var ideferred = Q.defer(); //inner deferred for resolve of async each
        //async crawling through dir
        async.each(list, function(item, done) {

            //stat current item in dirlist
            return Q.nfcall(fs.stat, path.join(startPath, item))
                .then(function(stat) {
                    //check if item is a directory
                    if (stat.isDirectory()) {
                        //recursive!! find files in subdirectory
                        return findFiles(path.join(startPath, item), filter, files)
                            .catch(function(error){
                                console.log("could not read path: " + error.toString());
                            })
                            .finally(function() {
                                //resolve async job after promise of subprocess of finding files has been resolved
                                return done();
                             });
                    //check if item is a file, that matches the filter and add it to files array
                    } else if (item.indexOf(filter) >= 0) {
                        files.push(path.join(startPath, item));
                        return done();
                    //file is no directory and does not match the filefilter -> don't do anything
                    } else {
                        return done();
                    }
                })
                .catch(function(error){
                    ideferred.reject("Could not stat: " + error.toString());
                });
        }, function() {
            return ideferred.resolve(); //async each has finished, so resolve inner deferred
        });
        return ideferred.promise;
    }).then(function() {
        //here you could do anything with the files of this recursion step (otherwise you would only need ONE deferred)
        return deferred.resolve(files); //resolve main deferred
    }).catch(function(error) {
        deferred.reject("Could not read dir: " + error.toString());
        return
    });
    return deferred.promise;
}


findFiles(process.env.START, process.env.FILTER, [])
    .then(function(files){
        console.log(files);
    })
    .catch(function(error){
        console.log("Problem finding files: " + error);
})

4
回调地狱的一个很好的例子!:)
Afshin Moazami

2
你是对的,不会再这样了:D也许我会在第二天找到时间,用async / await解决它,以显示差异。
Christoph Johannsdotter '17

0

安装

您可以安装该软件包步行同步

yarn add walk-sync

用法

const walkSync = require("walk-sync");
const paths = walkSync("./project1/src", {globs: ["**/*.html"]});
console.log(paths);   //all html file path array

-2

旧的帖子,但是ES6现在可以使用includes方法立即解决此问题。

let files = ['file.json', 'other.js'];

let jsonFiles = files.filter(file => file.includes('.json'));

console.log("Files: ", jsonFiles) ==> //file.json

之所以要对此表示赞成,是因为我正在使用file.readdirSync并且需要一种简单的方法来按扩展名过滤掉文件。我认为这可以回答此线程中部分问题,但可能无法解决所有问题。仍然值得考虑。
justinpage
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.