简体   繁体   中英

Javascript,Nodejs: search for a specific word string in files

i'm trying to make an app that searches for all files contains a specified string under the current directory/subdirectory.

as i understand it means i need to create a read stream, loop it, load the read data to an array, if the word found give __filename, dirname and if ! not found message.

unfortunately, i could not make it work... any clue?

 var path = require('path'), fs=require('fs'); function fromDir(startPath,filter,ext){ if (!fs.existsSync(startPath)){ console.log("no dir ",startPath); return; }; var files=fs.readdirSync(startPath); let found = files.find((file) => { let thisFilename = path.join(startPath, file); let stat = fs.lstatSync(thisFilename); var readStream = fs.createReadStream(fs); var readline = require('readline'); if (stat.isDirectory()) { fromDir(thisFilename, filename,readline, ext); } else { if (path.extname(createReadStream) === ext && path.basename(thisFilename, ext) === filename) { return true; } } }); console.log('-- your word has found on : ',filename,__dirname); } if (!found) { console.log("Sorry, we didn't find your term"); } } fromDir('./', process.argv[3], process.argv[2]);

Because not everything was included in the question, I made an assumption:

We are looking for full words (if that's not the case, replace the regex with a simple indexOf() ).

Now, I've split the code into two functions - to make it both more readable and easier to recursively find the files.

Synchronous version:

const path = require('path');
const fs = require('fs');

function searchFilesInDirectory(dir, filter, ext) {
    if (!fs.existsSync(dir)) {
        console.log(`Specified directory: ${dir} does not exist`);
        return;
    }

    const files = getFilesInDirectory(dir, ext);

    files.forEach(file => {
        const fileContent = fs.readFileSync(file);
        
        // We want full words, so we use full word boundary in regex.
        const regex = new RegExp('\\b' + filter + '\\b');
        if (regex.test(fileContent)) {
            console.log(`Your word was found in file: ${file}`);
        }
    });
}

// Using recursion, we find every file with the desired extention, even if its deeply nested in subfolders.
function getFilesInDirectory(dir, ext) {
    if (!fs.existsSync(dir)) {
        console.log(`Specified directory: ${dir} does not exist`);
        return;
    }

    let files = [];
    fs.readdirSync(dir).forEach(file => {
        const filePath = path.join(dir, file);
        const stat = fs.lstatSync(filePath);
        
        // If we hit a directory, apply our function to that dir. If we hit a file, add it to the array of files.
        if (stat.isDirectory()) {
            const nestedFiles = getFilesInDirectory(filePath, ext);
            files = files.concat(nestedFiles);
        } else {
            if (path.extname(file) === ext) {
                files.push(filePath);
            }
        }
    });

    return files;
}

Async version - because async is cool:

const path = require('path');
const fs = require('fs');
const util = require('util');

const fsReaddir = util.promisify(fs.readdir);
const fsReadFile = util.promisify(fs.readFile);
const fsLstat = util.promisify(fs.lstat);

async function searchFilesInDirectoryAsync(dir, filter, ext) {  
    const found = await getFilesInDirectoryAsync(dir, ext);

    for (file of found) {
        const fileContent = await fsReadFile(file);

        // We want full words, so we use full word boundary in regex.
        const regex = new RegExp('\\b' + filter + '\\b');
        if (regex.test(fileContent)) {
            console.log(`Your word was found in file: ${file}`);
        }
    };
}

// Using recursion, we find every file with the desired extention, even if its deeply nested in subfolders.
async function getFilesInDirectoryAsync(dir, ext) {
    let files = [];
    const filesFromDirectory = await fsReaddir(dir).catch(err => {
        throw new Error(err.message);
    });

    for (let file of filesFromDirectory) {
        const filePath = path.join(dir, file);
        const stat = await fsLstat(filePath);

        // If we hit a directory, apply our function to that dir. If we hit a file, add it to the array of files.
        if (stat.isDirectory()) {
            const nestedFiles = await getFilesInDirectoryAsync(filePath, ext);
            files = files.concat(nestedFiles);
        } else {
            if (path.extname(file) === ext) {
                files.push(filePath);
            }
        }
    };

    return files;
}

If you have not worked with / understand async/await yet, it is a great step to take and learn it as soon as possible. Trust me, you will love not seeing those ugly callbacks again!

UPDATE: As you pointed in comments, you want it to execute the function after running node process on the file. You also want to pass the function parameters as node 's arguments.

To do that, at the end of your file, you need to add:

searchFilesInDirectory(process.argv[2], process.argv[3], process.argv[4]);

This extracts our arguments and passes them to the function.

With that, you can call our process like so (example arguments):

node yourscriptname.js ./ james .txt

Personally, if I were to write this, I would leverage the beauty of asynchronous code, and Node.js's async / await .

As a very side note:

You can easily improve readability of your code, if you add proper formatting to it. Don't get me wrong, it's not terrible - but it can be improved:

  1. Use spaces OR newlines after commas.
  2. Use spaces around equality operators and arithmetic operators.

As long as you are consistent with formatting, everything looks much better.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM