znajdź pliki według rozszerzenia, * .html w folderze w nodejs

89

Chciałbym znaleźć wszystkie pliki * .html w folderze src i wszystkie jego podfoldery przy użyciu nodejs. Jak najlepiej to zrobić?

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

Myślę, że wielu programistów powinno mieć świetne i sprawdzone rozwiązanie i lepiej z niego korzystać niż pisać samodzielnie.

Nicolas S.Xu
źródło
Jeśli chcesz wyszukiwać pliki według wyrażenia regularnego, użyj biblioteki file-regex , która jednocześnie wykonuje cykliczne wyszukiwanie plików.
Akash Babu

Odpowiedzi:

90

node.js, rekurencyjna prosta funkcja:

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

dodaj RegExp, jeśli chcesz uzyskać fantazyjny, i wywołanie zwrotne, aby uczynić go ogólnym.

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);
});
Lucio M. Tato
źródło
wielkie dzięki za kod demo! Dodałem coś do twojego kodu i działa świetnie! Sprawdziłem również Twój projekt LiteScript i jest niesamowity. Oznaczyłem to na githubie!
Nicolas S.Xu
Ładny mały skrypt do znajdowania nazw plików również bez rozszerzenia - w moim przypadku miałem kilka JPEG i musiałem znaleźć, czy oryginalny plik w innym katalogu to png czy jpeg, to pomaga
Ricky Odin Matthews
78

lubię używać pakietu glob :

const glob = require('glob');

glob(__dirname + '/**/*.html', {}, (err, files)=>{
  console.log(files)
})
David Cheung
źródło
1
Zwykle nie jest fanem pakietów do prostych rzeczy, ale to tylko kwestia czasu, zanim glob będzie miał wbudowaną implementację node js. To trochę staje się wyrażeniem regularnym wyboru plików.
Seph Reed
27

Co, poczekaj ?! ... Okej, może to ma większy sens również dla kogoś innego.

[ nodejs 7 mind you]

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

Zrób cokolwiek z wyrażeniem regularnym, uczyń z niego argument, który ustawisz w funkcji z wartością domyślną itp.

Mistrz James
źródło
2
Spowoduje to uzyskanie tylko pasujących plików w katalogu głównym.
dreamerkumar
6
Próbowałem edytować i zostałem odrzucony, z czym się nie zgadzam. Oto moja propozycja: stackoverflow.com/review/suggested-edits/19188733 wl ma sens. Brakuje również importu dla fs. Trzy potrzebne linie to: 1. const fs = require('fs');2. const dirCont = fs.readdirSync( dir );3.const files = dirCont.filter( ( elm ) => /.*\.(htm?html)/gi.test(elm) );
Avindra Goolcharan
racja przepraszam wl.fs to miejsce, w którym zapisałem bibliotekę fs przez import.
Master James
o, import to prawdopodobnie moja własna funkcja niestandardowa, która na razie wskazuje na wymaganie, więc upewnij się, że używasz require lub cokolwiek musisz zrobić.
Master James
13

Na podstawie kodu Lucio stworzyłem moduł. Zwróci plik z wszystkimi plikami z określonymi rozszerzeniami pod jednym. Po prostu opublikuj to tutaj, na wypadek gdyby ktoś tego potrzebował.

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;
Nicolas S.Xu
źródło
12

Możesz to zrobić za pomocą Filehound .

Na przykład: znajdź wszystkie pliki .html w / 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);
});

Więcej informacji (i przykładów) można znaleźć w dokumentach: https://github.com/nspragg/filehound

Zastrzeżenie : jestem autorem.

nickool
źródło
8

Spojrzałem na powyższe odpowiedzi i wymieszałem ze sobą tę wersję, która działa dla mnie:

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

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

Ten test zwróci tablicę nazw plików z plików znalezionych w folderze w ścieżce ./testdata. Praca na węźle w wersji 8.11.3.

Netsi1964
źródło
1
Dodałbym $ na końcu RegExp:.*\.(${extension})$
Eugene
3

Możesz w tym celu skorzystać z pomocy systemu operacyjnego. Oto rozwiązanie wieloplatformowe:

1. Funkcja poniżej używa lsi dirnie przeszukuje rekurencyjnie, ale ma ścieżki względne

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. Funkcja poniżej używa findi dir, wyszukuje rekurencyjnie, ale w oknach ma ścieżki bezwzględne

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);
})
Emil Condrea
źródło
1
Nigdy nie myślałem, że można to zrobić w ten sposób, ponieważ nie jestem zaznajomiony z require ('child_process'). Exec, ale wygląda to bardzo dobrze i inspiruje we mnie wiele myśli. Dziękuję Ci!
Nicolas S.Xu
2
To nie jest sposób na zrobienie tego "używając nodejs". To jest używanie systemu operacyjnego, uruchamianie innego procesu itp. Błąd również kończy się niepowodzeniem, jeśli katalog kończy się na „.html”, np .: files.html /
Lucio M. Tato
@ LucioM.Tato możesz określić typ pliku podczas wyszukiwania. Istnieje wiele rozwiązań problemu, jeśli ktoś nie pasuje do twojego pomysłu, to po prostu nie oznacza, że ​​jest zły, po prostu jest inny. Ta odpowiedź dowodzi, że możesz ponownie wykorzystać istniejące rozwiązania bez względu na używany język skryptowy.
Emil Condrea
Oczywiście nie ma w tym nic złego w iterowaniu po katalogu i znajdowaniu plików z określonym rozszerzeniem, ale chciałem tylko otrzymać od systemu operacyjnego wszystkie te informacje, ponieważ wiedziałem, że może to zrobić. :)
Emil Condrea
@EmilCondrea, IHMO to nie jest „używanie węzła”, o co prosił OP. W każdym razie usunę głos przeciw, jeśli ci to przeszkadza.
Lucio M. Tato
3

Poniższy kod wyszukuje rekursywnie wewnątrz ./ (odpowiednio go zmień) i zwraca tablicę bezwzględnych nazw plików kończących się na .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);
Nikhil
źródło
2

Nie można dodać komentarza ze względu na reputację, ale zwróć uwagę na następujące kwestie:

Użycie fs.readdir lub node-glob do znalezienia zestawu symboli wieloznacznych w folderze zawierającym 500 000 plików zajęło około 2 s. Użycie exec z DIR zajęło ~ 0,05s (nierekurencyjne) lub ~ 0,45s (rekurencyjne). (Szukałem ~ 14 plików pasujących do mojego wzorca w jednym katalogu).

Do tej pory nie udało mi się znaleźć żadnej implementacji nodejs, która wykorzystuje niskopoziomowe symbole wieloznaczne systemu operacyjnego do wyszukiwania wydajności. Ale powyższy kod oparty na DIR / ls działa wspaniale w oknach pod względem wydajności. Jednak linux będzie działał bardzo wolno w przypadku dużych katalogów.

Simon H.
źródło
Rzeczywiście interesujące.
philk
Uwaga Widzę, że są nowe funkcje w najnowszym module nodejs fs (12.13+? Iterowany katalog fns?). Nie próbowałem ich jeszcze, ponieważ na razie utknąłem na 6.9.11; będzie interesujące zobaczyć, czy zapewniają one jakieś nowe przydatne funkcje. Myśląc teraz o moim stanowisku; Należy również wziąć pod uwagę buforowanie systemu operacyjnego. Moje 0,05s prawdopodobnie zostałoby zmierzone PO kilkukrotnym uruchomieniu. Zastanawiam się, jaka jest PIERWSZA prędkość „DIR”?
Simon H
1

moje dwa pensy, używając mapy zamiast pętli 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);
jset74
źródło
1

Spójrz na file-regex

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

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

Powyższy fragment spowoduje wydrukowanie wszystkich jsplików w bieżącym katalogu.

Akash Babu
źródło
To faktycznie najłatwiejsze rozwiązanie.
kyeno
0

Właśnie zauważyłem, że używasz metod sync fs, które mogą blokować twoją aplikację, oto sposób asynchroniczny oparty na obietnicy z użyciem async i q , możesz go wykonać za pomocą START = / myfolder FILTER = ". Jpg" węzeł myfile.js, zakładając, że umieścisz następujący kod w pliku o nazwie 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);
})
Christoph Johannsdotter
źródło
4
Świetny przykład piekła oddzwonienia! :)
Afshin Moazami,
2
masz rację, nie zrobiłbym tego ponownie w ten sposób: D Może znajdę czas w następnych dniach, rozwiązując to za pomocą async / await, aby pokazać różnicę.
Christoph Johannsdotter,
0

zainstalować

możesz zainstalować ten pakiet walk-sync wg

yarn add walk-sync

Stosowanie

const walkSync = require("walk-sync");
const paths = walkSync("./project1/src", {globs: ["**/*.html"]});
console.log(paths);   //all html file path array
Muhammad Numan
źródło
-2

Stary post, ale ES6 obsługuje teraz tę includesmetodę po wyjęciu z pudełka .

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

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

console.log("Files: ", jsonFiles) ==> //file.json
James
źródło
Głosuję za tym, ponieważ używałem file.readdirSynci potrzebowałem prostego sposobu na filtrowanie plików według rozszerzenia. Myślę, że to odpowiada na część pytania w tym wątku, ale może nie na wszystko. Nadal warto to rozważyć.
justinpage