buscar archivos por extensión, * .html en una carpeta en nodejs


89

Me gustaría encontrar todos los archivos * .html en la carpeta src y todas sus subcarpetas usando nodejs. ¿Cuál es la mejor manera de hacerlo?

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

Creo que muchos desarrolladores deberían tener una solución excelente y probada y es mejor usarla que escribir una yo mismo.


Si desea buscar archivos por expresiones regulares, utilice la biblioteca file-regex , que realiza búsquedas de archivos recursivas al mismo tiempo.
Akash Babu

Respuestas:


90

node.js, función simple recursiva:

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

agregue RegExp si desea ser elegante y una devolución de llamada para hacerlo genérico.

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

¡Muchas gracias por el código de demostración! ¡Agregué algo en la parte superior de su código y funciona muy bien! También verifiqué su proyecto de LiteScript y es asombroso. ¡Lo he protagonizado en github!
Nicolas S.Xu

Un pequeño y agradable script para encontrar nombres de archivo sin extensión también: en mi caso, tenía algunos archivos JPEG y necesitaba encontrar si el archivo original en un directorio diferente era png o jpeg, esto ayuda
Ricky Odin Matthews

78

me gusta usar el paquete glob :

const glob = require('glob');

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

1
Normalmente no soy un fanático de los paquetes para cosas simples, pero es solo cuestión de tiempo antes de que glob tenga una implementación de nodo js incorporada. Esto se está convirtiendo en la expresión regular de la selección de archivos.
Seph Reed

27

¡¿Qué, espera ?! ... Está bien, tal vez esto también tenga más sentido para alguien más.

[ nodejs 7 fíjate]

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

Haga lo que sea con regex, conviértalo en un argumento que establezca en la función con un valor predeterminado, etc.


2
Esto solo obtendrá archivos coincidentes en el directorio raíz.
dreamerkumar

6
Intenté editar y me rechazaron, con lo que no estoy de acuerdo. Aquí está mi propuesta: stackoverflow.com/review/suggested-edits/19188733 wl tiene mucho sentido. Además, falta la importación de fs. Las tres líneas que necesita son: 1. const fs = require('fs');2. const dirCont = fs.readdirSync( dir );3.const files = dirCont.filter( ( elm ) => /.*\.(htm?html)/gi.test(elm) );
Avindra Goolcharan

Lo siento, wl.fs es donde almacené la lib fs a través de la importación.
Master James

oh, la importación es probablemente mi propia función personalizada que apunta a requerir también por ahora, así que asegúrese de usarlo o lo que sea que tenga que hacer.
Master James

13

Basado en el código de Lucio, hice un módulo. Regresará con todos los archivos con extensiones específicas debajo de uno. Solo publícalo aquí en caso de que alguien lo necesite.

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

Puede utilizar Filehound para hacer esto.

Por ejemplo: busque todos los archivos .html en / 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);
});

Para obtener más información (y ejemplos), consulte los documentos: https://github.com/nspragg/filehound

Descargo de responsabilidad : soy el autor.


8

He mirado las respuestas anteriores y he mezclado esta versión que funciona para mí:

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

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

Esta prueba devolverá una matriz de nombres de archivos de los archivos que se encuentran en la carpeta en la ruta ./testdata. Trabajando en la versión de nodo 8.11.3.


1
Agregaría $ al final de la expresión regular:.*\.(${extension})$
Eugene

3

Puede utilizar la ayuda del sistema operativo para esto. Aquí hay una solución multiplataforma:

1. La función de abajo usa lsy dirno busca de forma recursiva, pero tiene rutas relativas

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 función de abajo usa findy dir, busca de forma recursiva, pero en Windows tiene rutas absolutas.

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
Nunca pensé que se podría hacer de esta manera, ya que no estoy familiarizado con require ('child_process'). Exec, pero se ve muy bien e inspira muchos pensamientos en mí. ¡Gracias!
Nicolas S.Xu

2
Esta no es la forma de hacerlo "usando nodejs". Esto es usar el sistema operativo, iniciar otro proceso, etc. También falla si hay un directorio que termina en ".html", por ejemplo: files.html /
Lucio M. Tato

@ LucioM.Tato puede especificar el tipo de archivo al buscar. Hay muchas soluciones para un problema, si una no coincide con tu idea, no significa que esté mal, simplemente es diferente. Esta respuesta demuestra que puede reutilizar las soluciones existentes sin importar qué lenguaje de secuencias de comandos se utilice.
Emil Condrea

Por supuesto, no hay nada de malo en iterar sobre un directorio y encontrar los archivos con cierta extensión, pero solo quería recibir del sistema operativo toda esta información porque sabía que él podía hacerlo. :)
Emil Condrea

@EmilCondrea, IHMO, esto no es "usar nodo" como pidió el OP. De todos modos, eliminaré el voto negativo si eso te molesta.
Lucio M. Tato

3

El siguiente código hace una búsqueda recursiva dentro de ./ (cámbielo apropiadamente) y devuelve una matriz de nombres de archivos absolutos que terminan en .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

No puedo agregar un comentario debido a la reputación, pero tenga en cuenta lo siguiente:

Usar fs.readdir o node-glob para encontrar un conjunto de archivos comodín en una carpeta de 500,000 archivos tomó ~ 2 s. El uso de exec con DIR tomó ~ 0.05s (no recursivo) o ~ 0.45s (recursivo). (Estaba buscando ~ 14 archivos que coincidan con mi patrón en un solo directorio).

Hasta ahora, no he podido encontrar ninguna implementación de nodejs que utilice comodines de SO de bajo nivel en busca de eficiencia. Pero el código anterior basado en DIR / ls funciona maravillosamente en Windows en términos de eficiencia. Linux find, sin embargo, probablemente será muy lento para directorios grandes.


Interesante, de hecho.
philk

Tenga en cuenta que veo que hay nuevas funciones en el último módulo nodejs fs (12.13+? Directorio iterado fns?). No los he probado todavía porque estoy bloqueado en 6.9.11 por ahora; Será interesante ver si proporcionan nuevas características útiles para esto. Pensando en mi publicación ahora; También se debe considerar el almacenamiento en caché del sistema operativo. Mis 0.05s probablemente se habrían medido DESPUÉS de haberlo ejecutado varias veces. Me pregunto cuál es la PRIMERA velocidad 'DIR'.
Simon H

1

mis dos centavos, usando map en lugar de for-loop

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

Eche un vistazo a file-regex

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

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

Este fragmento de código anterior imprimiría todos los jsarchivos en el directorio actual.


En realidad, esa es la solución más fácil que existe.
kyeno

0

Acabo de notar, que está utilizando métodos de sincronización fs, que podría bloquear la aplicación, aquí hay una manera asíncrona basada en el uso de la promesa asíncrono y q , se puede ejecutar con start = / = myfolder myfile.js FILTRO "jpg" nodo, suponiendo que pones el siguiente código en un archivo llamado 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 gran ejemplo del infierno de devolución de llamada! :)
Afshin Moazami

2
tienes razón, no lo volvería a hacer de esta manera: D Quizás encuentre tiempo los próximos días, resolviéndolo con async / await para mostrar la diferencia.
Christoph Johannsdotter

0

Instalar en pc

puede instalar este paquete walk-sync por

yarn add walk-sync

Uso

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

-2

Publicación anterior, pero ES6 ahora maneja esto fuera de la caja con el includesmétodo.

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

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

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

Voy a votar esto porque estaba usando file.readdirSyncy necesitaba una forma sencilla de filtrar archivos por extensión. Creo que esto responde parte de la pregunta en este hilo, pero tal vez no todo. Todavía vale la pena considerarlo.
justinpage
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.