найти файлы по расширению, * .html в папке в nodejs

89

Я хотел бы найти все файлы * .html в папке src и всех ее подпапках с помощью nodejs. Как лучше всего это сделать?

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 , которая выполняет рекурсивный поиск файлов одновременно.
Акаш Бабу

Ответы:

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, и он потрясающий. Я снял его на гитхабе!
Николас S.Xu
Хороший маленький скрипт для поиска имен файлов без расширения - в моем случае у меня было несколько Jpeg, и мне нужно было выяснить, был ли исходный файл в другом каталоге png или jpeg, это помогает
Рики Один Мэтьюз
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);});

Делайте все, что с регулярным выражением, сделайте его аргументом, который вы установили в функции со значением по умолчанию и т. Д.

Мастер Джеймс
источник
2
Это приведет к получению только совпадающих файлов в корневом каталоге.
dreamerkumar 03
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 через import.
Мастер Джеймс
о, импорт, вероятно, моя собственная пользовательская функция, которая тоже указывает на необходимость, поэтому обязательно используйте require или все, что вам нужно сделать.
Мастер Джеймс
13

На основе кода Люцио я сделал модуль. Он вернет все файлы с определенными расширениями под одним. Просто разместите его здесь, если кому-нибудь это понадобится.

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 .

Например: найти все файлы .html в / tmp:

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.

Нетси1964
источник
1
Я бы добавил $ в конце RegExp:.*\.(${extension})$
Eugene
3

Для этого вы можете использовать справку ОС. Вот кроссплатформенное решение:

1. Приведенная ниже функция использует lsи, dirа не выполняет рекурсивный поиск, но имеет относительные пути.

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. Приведенная ниже функция использует findи dir, выполняет рекурсивный поиск, но в окнах у нее есть абсолютные пути.

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
2
Это не способ сделать это с помощью nodejs. Это использование ОС, запуск другого процесса и т. Д. Это также не удается, если каталог заканчивается на «.html», например: files.html /
Люцио М. Тато
@ LucioM. Чтобы вы могли указать тип файла при поиске. Есть много решений проблемы, если одно не соответствует вашей идее, это не значит, что оно неправильное, это просто другое. Этот ответ доказывает, что вы можете повторно использовать существующие решения независимо от того, какой язык сценариев используется.
Эмиль Кондреа
Конечно, нет ничего плохого в переборе каталога и поиске файлов с определенным расширением, но я просто хотел получить от ОС всю эту информацию, потому что знал, что он может это сделать. :)
Эмиль Кондреа
@EmilCondrea, IHMO, это не "использование узла", как просил ОП. В любом случае, я уберу голос против, если вас это беспокоит.
Лучио М. Тато
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 секунды. Использование exec с DIR заняло ~ 0,05 с (нерекурсивный) или ~ 0,45 с (рекурсивный). (Я искал ~ 14 файлов, соответствующих моему шаблону в одном каталоге).

Пока мне не удалось найти какую-либо реализацию nodejs, которая использует поиск подстановочных знаков ОС низкого уровня для повышения эффективности. Но приведенный выше код на основе DIR / ls прекрасно работает в Windows с точки зрения эффективности. linux find, однако, скорее всего, будет очень медленным для больших каталогов.

Саймон Х
источник
Действительно интересно.
philk
Обратите внимание: я вижу, что в последнем модуле nodejs fs есть новые функции (12.13+? Итерационный каталог fns?). Я их еще не пробовал, потому что пока застрял на 6.9.11; будет интересно посмотреть, предоставляют ли они для этого какие-либо новые полезные функции. Думаю о моем посте сейчас; Также следует учитывать кеширование ОС. Мои 0,05, вероятно, были бы измерены ПОСЛЕ того, как я запустил его несколько раз. Интересно, какая скорость ПЕРВАЯ «DIR»?
Саймон Х,
1

мои два пенса, используя карту вместо цикла

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);
jset74
источник
1

Взгляните на file-regex

let findFiles = require('file-regex')
let pattern = '\.js'

findFiles(__dirname, pattern, (err, files) => {  
   console.log(files);
})

Этот фрагмент выше распечатает все jsфайлы в текущем каталоге.

Акаш Бабу
источник
На самом деле это самое простое решение.
kyeno
0

Я только что заметил, что вы используете методы sync fs, которые могут заблокировать ваше приложение, вот асинхронный способ на основе обещаний с использованием async и q , вы можете выполнить его с помощью START = / myfolder FILTER = ". Jpg" node 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
Отличный пример ада обратного вызова! :)
Афшин Моазами
2
вы правы, не стал бы делать это снова: D Может быть, я найду время в следующие дни, решив его с помощью async / await, чтобы показать разницу.
Christoph Johannsdotter
0

Установить

вы можете установить этот пакет 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
Мухаммад Нуман
источник
-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