【问题标题】:find files by extension, *.html under a folder in nodejs在nodejs中的文件夹下按扩展名*.html查找文件
【发布时间】:2014-08-23 09:38:56
【问题描述】:

我想使用 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库,它同时进行递归文件搜索。

标签: node.js find file-extension


【解决方案1】:

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 上给它加了星标!
  • 不错的小脚本,用于查找没有扩展名的文件名 - 在我的情况下,我有一些 Jpeg,需要查找不同目录中的原始文件是 png 还是 jpeg,这有帮助
  • 如果有扩展数组怎么办我如何过滤它们例如:const extName = [".html", ".htm"]? @lucio
  • stackoverflow.com/a/62695186/8079868 我认为这个答案应该被接受
  • 请注意,filename.indexOf(filter)&gt;=0 也将匹配 test.html.yaml
【解决方案2】:

我喜欢使用glob 包:

const glob = require('glob');

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

【讨论】:

  • 通常不喜欢简单的包,但 glob 有一个内置的 node js 实现只是时间问题。这有点成为文件选择的正则表达式。
  • 这是否适用于 2 种不同的文件类型?
  • 是的,您可以根据文档使用@(pattern|pattern|pattern),因此/**/*.@(js|html) 之类的内容将匹配github.com/isaacs/node-glob#glob-primer 两种类型之一
【解决方案3】:

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

[nodejs 7请注意]

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

用正则表达式做任何事情,让它成为你在函数中设置的参数,并使用默认值等。

【讨论】:

  • 这只会获取根目录中的匹配文件。
  • 我尝试编辑但被拒绝,我不同意。这是我的建议:stackoverflow.com/review/suggested-edits/19188733wl 无论如何都很有意义。 fs 的导入也丢失了。您需要的三行是:1.const fs = require('fs'); 2.const dirCont = fs.readdirSync( dir ); 3.const files = dirCont.filter( ( elm ) =&gt; /.*\.(htm?html)/gi.test(elm) );
  • 对,对不起 wl.fs 是我通过导入存储 fs 库的地方。
  • 哦 import 可能是我自己的自定义函数,现在也指向 require ,所以请务必使用 require 或您必须做的任何事情。
【解决方案4】:

根据 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;

【讨论】:

    【解决方案5】:

    您可以使用 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

    免责声明:我是作者。

    【讨论】:

      【解决方案6】:

      我已经查看了上述答案,并将这个适合我的版本混合在一起:

      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 上工作。

      【讨论】:

      • 我会在 RegExp 的末尾添加 $:.*\.(${extension})$
      【解决方案7】:

      以下代码在 ./ 内进行递归搜索(适当更改)并返回以 .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);
      

      【讨论】:

        【解决方案8】:

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

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

        【讨论】:

        • 我从没想过可以这样做,因为我不熟悉 require('child_process').exec,但它看起来非常好,并激发了我很多想法。谢谢!
        • 这不是“使用nodejs”的方法。这是使用操作系统,启动另一个进程等。如果有一个以“.html”结尾的目录,它也会失败,例如:files.html/
        • @LucioM.Tato 您可以在搜索时指定文件类型。一个问题有很多解决方案,如果一个不符合你的想法,那并不意味着它是错误的,它只是不同。这个答案证明,无论使用哪种脚本语言,您都可以重用现有的解决方案。
        • 当然,遍历目录并查找具有特定扩展名的文件并没有错,但我只是想从操作系统接收所有这些信息,因为我知道他可以做到。 :)
        • @EmilCondrea,IHMO 这不是 OP 要求的“使用节点”。无论如何,如果这让您感到困扰,我会删除反对票。
        【解决方案9】:

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

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

        到目前为止,我还没有找到任何使用低级操作系统通配符搜索效率的 nodejs 实现。但就效率而言,上述基于 DIR/ls 的代码在 windows 中工作得非常好。但是,对于大型目录,linux 会找到 will likely be very slow

        【讨论】:

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

        看看file-regex

        let findFiles = require('file-regex')
        let pattern = '\.js'
        
        findFiles(__dirname, pattern, (err, files) => {  
           console.log(files);
        })
        

        上面的 sn-p 将打印当前目录中的所有js 文件。

        【讨论】:

        • 这实际上是最简单的解决方案。
        【解决方案11】:

        安装

        你可以通过walk-sync安装这个包

        yarn add walk-sync
        

        用法

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

        【讨论】:

        • 这是最简单的解决方案之一
        【解决方案12】:

        我的两便士,用 map 代替 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);
        

        【讨论】:

          【解决方案13】:

          我刚刚注意到,您正在使用同步 fs 方法,这可能会阻止您的应用程序,这是一种使用 asyncq 的基于 Promise 的异步方式,您可以执行使用 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);
          })
          

          【讨论】:

          • 回调地狱的一个很好的例子! :)
          • 你是对的,不会再这样做了:D 也许我会在接下来的几天里找到时间,用 async/await 解决它以显示差异。
          【解决方案14】:

          您可以编辑此代码以适合您的意图。我为 nodejs IO 操作使用了同步版本,以便在 node 继续执行下一行代码之前返回结果:

          const fs = require('fs');
          const path = require('path');
              
          // Path to the directory(folder) to look into
          const dirPath = path.resolve(`${__dirname}../../../../../tests_output`);
                  
          // Read all files with .html extension in the specified folder above
          const filesList = fs.readdirSync(dirPath, (err, files) => files.filter((e) => path.extname(e).toLowerCase() === '.html'));
                  
          // Read the content of the first file with .txt extension in the folder
          const data = fs.readFileSync(path.resolve(`${__dirname}../../../../../tests_output/${filesList[0]}`), 'utf8');
          
          res.writeHead(200, { 'Content-Type': 'text/html' });
          res.write(data);
          return res.end();
          

          【讨论】:

            【解决方案15】:

            对于无数可能的解决方案,我们还可以添加非常适合构建脚本目的的 fs-jetpack 库。

            const jetpack = require("fs-jetpack");
            
            // the sync way
            const files = jetpack.find("my_project", { matching: "*.html" });
            console.log(files);
            
            // or the async way
            jetpack.findAsync("my_project", { matching: "*.html" }).then(files => {
              console.log(files);
            });
            
            

            【讨论】:

              【解决方案16】:

              旧帖子,但 ES6 现在使用 includes 方法开箱即用地处理这个问题。

              let files = ['file.json', 'other.js'];
              
              let jsonFiles = files.filter(file => file.includes('.json'));
              
              console.log("Files: ", jsonFiles) ==> //file.json
              

              【讨论】:

              • 要对此表示赞成,因为我使用的是file.readdirSync,并且需要一种简单的方法来按扩展名过滤掉文件。我认为这回答了这个线程中的部分问题,但可能不是全部。还是值得考虑的。
              猜你喜欢
              • 2011-09-23
              • 1970-01-01
              • 2011-03-10
              • 1970-01-01
              • 1970-01-01
              • 2018-05-29
              • 1970-01-01
              • 1970-01-01
              • 2020-10-16
              相关资源
              最近更新 更多