ค้นหาไฟล์ตามนามสกุล * .html ภายใต้โฟลเดอร์ใน nodejs


92

ฉันต้องการค้นหาไฟล์ * .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);

ฉันคิดว่านักพัฒนาจำนวนมากควรมีโซลูชันที่ยอดเยี่ยมและผ่านการทดสอบแล้วและจะดีกว่าการเขียนด้วยตัวเอง


หากคุณต้องการค้นหาไฟล์ด้วย regex ให้ใช้ไลบรารีfile-regexซึ่งจะค้นหาไฟล์แบบวนซ้ำพร้อมกัน
Akash Babu

คำตอบ:


92

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!
Nicolas S.Xu

สคริปต์เล็ก ๆ น้อย ๆ ที่ดีสำหรับการค้นหาชื่อไฟล์ที่ไม่มีนามสกุล - ในกรณีของฉันฉันมี Jpegs และต้องการค้นหาว่าไฟล์ต้นฉบับใน dir อื่นเป็น png หรือ jpeg หรือไม่สิ่งนี้ช่วยได้
Ricky Odin Matthews

80

ฉันชอบใช้แพ็คเกจglob :

const glob = require('glob');

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

1
โดยปกติไม่ใช่แฟนของแพ็คเกจสำหรับสิ่งง่ายๆ แต่เป็นเพียงเรื่องของเวลาก่อนที่ glob จะมีการใช้งาน node js ในตัว นี่เป็นการกลายเป็น regexp ของการเลือกไฟล์
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);});

ทำอะไรก็ได้กับ regex ทำให้เป็นอาร์กิวเมนต์ที่คุณตั้งไว้ในฟังก์ชันด้วยค่าเริ่มต้นเป็นต้น


2
สิ่งนี้จะได้รับไฟล์ที่ตรงกันในไดเรกทอรีรากเท่านั้น
dreamerkumar

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 lib ผ่านการนำเข้า
Master James

โอ้การนำเข้าน่าจะเป็นฟังก์ชั่นที่กำหนดเองของฉันเองที่ชี้ว่าต้องใช้ในตอนนี้เช่นกันดังนั้นให้แน่ใจว่าต้องใช้หรืออะไรก็ตามที่คุณต้องทำ
Master James

13

จากรหัสของ 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;

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

Disclaimer : ฉันเป็นผู้เขียน


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


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ค้นหาซ้ำ ๆ แต่บน 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);
})

1
ฉันไม่เคยคิดว่ามันจะทำได้ขนาดนี้เพราะฉันไม่คุ้นเคยกับความต้องการ ('child_process') exec แต่มันดูดีมากและเป็นแรงบันดาลใจให้ฉันมีความคิดมากมายในตัวฉัน ขอขอบคุณ!
Nicolas S.Xu

2
นี่ไม่ใช่วิธีการ "ใช้ nodejs" กำลังใช้ระบบปฏิบัติการเปิดใช้กระบวนการอื่น ฯลฯ และจะล้มเหลวหากมี dir ที่ลงท้ายด้วย ".html" เช่น files.html /
Lucio M. Tato

@ LucioM Tato คุณสามารถระบุประเภทไฟล์เมื่อค้นหา มีวิธีแก้ปัญหามากมายหากวิธีหนึ่งไม่ตรงกับความคิดของคุณมันก็ไม่ได้หมายความว่ามันผิดมันแตกต่างกัน คำตอบนี้พิสูจน์ได้ว่าคุณสามารถนำโซลูชันที่มีอยู่กลับมาใช้ใหม่ได้ไม่ว่าจะใช้ภาษาสคริปต์ใดก็ตาม
Emil Condrea

แน่นอนว่าไม่มีอะไรผิดปกติกับการวนซ้ำในไดเรกทอรีและค้นหาไฟล์ที่มีนามสกุลที่แน่นอน แต่ฉันแค่ต้องการรับข้อมูลทั้งหมดนี้จากระบบปฏิบัติการเพราะฉันรู้ว่าเขาทำได้ :)
Emil Condrea

@EmilCondrea, IHMO นี่ไม่ใช่ "การใช้โหนด" ตามที่ OP ถาม อย่างไรก็ตามฉันจะลบการโหวตลงหากมันรบกวนคุณ
Lucio M. Tato

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

ลองดูfile-regex

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

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

ข้อมูลโค้ดด้านบนนี้จะพิมพ์jsไฟล์ทั้งหมดในไดเร็กทอรีปัจจุบัน


นั่นเป็นวิธีแก้ปัญหาที่ง่ายที่สุด
kyeno

2

ไม่สามารถเพิ่มความคิดเห็นได้เนื่องจากชื่อเสียง แต่โปรดสังเกตสิ่งต่อไปนี้:

การใช้ fs.readdir หรือ node-glob เพื่อค้นหาชุดไฟล์ไวด์การ์ดในโฟลเดอร์ 500,000 ไฟล์ใช้เวลาประมาณ 2 วินาที การใช้ exec กับ DIR ใช้เวลา ~ 0.05s (non recursive) หรือ ~ 0.45s (recursive) (ฉันกำลังมองหา ~ 14 ไฟล์ที่ตรงกับรูปแบบของฉันในไดเร็กทอรีเดียว)

จนถึงขณะนี้ฉันไม่พบการใช้งาน nodejs ใด ๆ ที่ใช้สัญลักษณ์แทน OS ระดับต่ำในการค้นหาประสิทธิภาพ แต่โค้ดที่ใช้ DIR / ls ข้างต้นทำงานได้อย่างยอดเยี่ยมใน windows ในแง่ของประสิทธิภาพ linux find จะช้ามากสำหรับไดเร็กทอรีขนาดใหญ่


น่าสนใจแน่นอน
philk

หมายเหตุฉันเห็นว่ามีฟังก์ชันใหม่ในโมดูล nodejs fs ล่าสุด (12.13+? iterated directory fns?) ฉันยังไม่ได้ลองเพราะตอนนี้ฉันติดอยู่ที่ 6.9.11; จะน่าสนใจเพื่อดูว่ามีคุณสมบัติใหม่ ๆ ที่เป็นประโยชน์สำหรับสิ่งนี้หรือไม่ กำลังคิดเกี่ยวกับโพสต์ของฉันตอนนี้ ควรพิจารณาการแคชระบบปฏิบัติการด้วย 0.05s ของฉันน่าจะถูกวัดหลังจากที่เรียกใช้หลายครั้ง ฉันสงสัยว่า FIRST 'DIR' คืออะไร?
Simon H

1

เพนนีสองตัวของฉันโดยใช้แผนที่แทน 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);

0

ฉันเพิ่งสังเกตว่าคุณใช้วิธีการ sync fs ซึ่งอาจบล็อกแอปพลิเคชันของคุณนี่คือวิธี async ตามสัญญาโดยใช้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
ตัวอย่างที่ดีของการเรียกกลับนรก! :)
Afshin Moazami

2
คุณพูดถูกจะไม่ทำแบบนี้อีก: D บางทีฉันอาจจะหาเวลาในวันถัดไปแก้ด้วย async / รอเพื่อแสดงความแตกต่าง
Christoph Johannsdotter


-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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.