rechercher des fichiers par extension, * .html sous un dossier dans nodejs


89

Je voudrais trouver tous les fichiers * .html dans le dossier src et tous ses sous-dossiers en utilisant nodejs. Quelle est la meilleure façon de procéder?

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

Je pense que beaucoup de développeurs devraient avoir une solution excellente et testée et il vaut mieux l'utiliser que d'en écrire une moi-même.


Si vous souhaitez rechercher des fichiers par regex, utilisez la bibliothèque file-regex , qui effectue une recherche de fichiers récursive simultanément.
Akash Babu

Réponses:


90

node.js, fonction simple récursive:

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

ajoutez RegExp si vous voulez avoir de la fantaisie, et un rappel pour le rendre générique.

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

merci beaucoup pour le code de démonstration! J'ai ajouté quelque chose en plus de votre code et cela fonctionne très bien! J'ai également vérifié votre projet LiteScript, et c'est incroyable. Je l'ai mis en vedette sur github!
Nicolas S.Xu

Joli petit script pour trouver des noms de fichiers sans extension également - dans mon cas, j'avais des fichiers JPEG et j'avais besoin de trouver si le fichier original dans un répertoire différent était png ou jpeg, cela aide
Ricky Odin Matthews

78

j'aime utiliser le package glob :

const glob = require('glob');

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

1
Normalement, ce n'est pas un fan de packages pour des choses simples, mais ce n'est qu'une question de temps avant que glob n'ait une implémentation de node js intégrée. Cela devient en quelque sorte l'expression rationnelle de la sélection de fichiers.
Seph Reed

27

Quoi, accrochez-vous?! ... D'accord, peut-être que cela a plus de sens pour quelqu'un d'autre aussi.

[ nodejs 7 mind you]

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

Faites ce que vous voulez avec regex en faire un argument que vous définissez dans la fonction avec une valeur par défaut, etc.


2
Cela n'obtiendra que les fichiers correspondants dans le répertoire racine.
dreamerkumar

6
J'ai essayé de modifier et j'ai été rejeté, ce avec quoi je ne suis pas d'accord. Voici ma proposition: stackoverflow.com/review/suggested-edits/19188733 wl est si logique que ce soit. L'import pour fs est également manquant. Les trois lignes dont vous avez besoin sont: 1. const fs = require('fs');2. const dirCont = fs.readdirSync( dir );3.const files = dirCont.filter( ( elm ) => /.*\.(htm?html)/gi.test(elm) );
Avindra Goolcharan

désolé wl.fs est l'endroit où j'ai stocké la lib fs via l'importation.
Master James

oh import est probablement ma propre fonction personnalisée qui pointe aussi pour require pour le moment, alors utilisez-vous vraiment require ou tout ce que vous avez à faire.
Master James

13

Sur la base du code de Lucio, j'ai créé un module. Il renverra un avec tous les fichiers avec des extensions spécifiques sous celui-ci. Postez-le ici au cas où quelqu'un en aurait besoin.

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

Vous pouvez utiliser Filehound pour ce faire.

Par exemple: recherchez tous les fichiers .html dans / 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);
});

Pour plus d'informations (et des exemples), consultez la documentation: https://github.com/nspragg/filehound

Avertissement : je suis l'auteur.


8

J'ai regardé les réponses ci-dessus et j'ai mélangé cette version qui fonctionne pour moi:

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

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

Ce test renverra un tableau de noms de fichiers à partir des fichiers trouvés dans le dossier au chemin ./testdata. Travailler sur la version 8.11.3 du nœud.


1
J'ajouterais $ à la fin de la RegExp:.*\.(${extension})$
Eugene

3

Vous pouvez utiliser l'aide du système d'exploitation pour cela. Voici une solution multiplateforme:

1. La fonction ci-dessous utilise lset diret ne recherche pas récursivement mais elle a des chemins relatifs

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. La fonction ci-dessous utilise findet dir, recherche récursivement mais sur Windows, elle a des chemins absolus

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
Je n'ai jamais pensé que cela pourrait être fait de cette façon, car je ne suis pas familier avec require ('child_process'). Exec, mais cela a l'air très bien et m'inspire beaucoup de pensées. Merci!
Nicolas S.Xu

2
Ce n'est pas la manière de le faire "en utilisant nodejs". Cela utilise le système d'exploitation, lance un autre processus, etc. Cela échoue également s'il y a un répertoire se terminant par ".html", par exemple: files.html /
Lucio M. Tato

@ LucioM.To vous pouvez spécifier le type de fichier lors de la recherche. Il y a beaucoup de solutions à un problème, si l'une ne correspond pas à votre idée, cela ne signifie tout simplement pas que c'est faux, c'est juste différent. Cette réponse prouve que vous pouvez réutiliser les solutions existantes quel que soit le langage de script utilisé.
Emil Condrea

Bien sûr, il n'y a rien de mal à parcourir un répertoire et à trouver les fichiers avec une certaine extension, mais je voulais juste recevoir du système d'exploitation toutes ces informations parce que je savais qu'il pouvait le faire. :)
Emil Condrea

@EmilCondrea, IHMO ce n'est pas "en utilisant le nœud" comme l'OP l'a demandé. Quoi qu'il en soit, je retirerai le vote défavorable si cela vous dérange.
Lucio M. Tato

3

Le code suivant effectue une recherche récursive dans ./ (modifiez-le de manière appropriée) et retourne un tableau de noms de fichiers absolus se terminant par .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

Impossible d'ajouter un commentaire en raison de la réputation, mais remarquez ce qui suit:

L'utilisation de fs.readdir ou node-glob pour trouver un ensemble de fichiers génériques dans un dossier de 500 000 fichiers a pris ~ 2s. L'utilisation de exec avec DIR a pris ~ 0,05 s (non récursif) ou ~ 0,45 s (récursif). (Je cherchais ~ 14 fichiers correspondant à mon modèle dans un seul répertoire).

Jusqu'à présent, je n'ai trouvé aucune implémentation de nodejs qui utilise des caractères génériques de bas niveau pour le système d'exploitation à la recherche d'efficacité. Mais le code basé sur DIR / ls ci-dessus fonctionne à merveille dans Windows en termes d'efficacité. linux find, cependant, sera probablement très lent pour les grands répertoires.


Intéressant, en effet.
philk

Remarque Je vois qu'il y a de nouvelles fonctions dans le dernier module nodejs fs (12.13+? Iterated directory fns?). Je ne les ai pas encore essayés car je suis bloqué sur 6.9.11 pour le moment; sera intéressant de voir s'ils fournissent de nouvelles fonctionnalités utiles pour cela. En pensant à mon message maintenant; La mise en cache du système d'exploitation doit également être envisagée. Mes 0,05s auraient probablement été mesurés APRÈS l'avoir exécuté plusieurs fois. Je me demande quelle est la première vitesse «DIR»?
Simon H le

1

mes deux pence, en utilisant la carte à la place de la boucle 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);

1

Jetez un œil à file-regex

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

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

Cet extrait de code ci-dessus afficherait tous les jsfichiers du répertoire actuel.


C'est en fait la solution la plus simple.
kyeno

0

Je viens de remarquer que vous utilisez des méthodes sync fs, qui pourraient bloquer votre application, voici une manière asynchrone basée sur la promesse utilisant async et q , vous pouvez l'exécuter avec START = / myfolder FILTER = ". Jpg" node myfile.js, en supposant que vous mettez le code suivant dans un fichier appelé 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
Un excellent exemple de l'enfer du rappel! :)
Afshin Moazami

2
vous avez raison, je ne le referais pas de cette façon: D Peut-être que je trouverai le temps les prochains jours, en le résolvant avec async / await pour montrer la différence.
Christoph Johannsdotter

0

Installer

vous pouvez installer ce package walk-sync en

yarn add walk-sync

Usage

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

-2

Ancien poste, mais ES6 gère maintenant cela hors de la boîte avec la includesméthode.

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

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

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

J'allais voter pour cela parce que j'utilisais file.readdirSyncet j'avais besoin d'un moyen simple de filtrer les fichiers par extension. Je pense que cela répond à une partie de la question de ce fil, mais peut-être pas à tout. Encore à considérer.
justinpage
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.