Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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]);
   
like image 542
Anatsu Avatar asked Feb 07 '18 11:02

Anatsu


People also ask

How do I search for a file in node JS?

To find the files that match a pattern using Node. js, install and use the glob module, passing it a pattern as the first parameter and a callback function as the second. The function gets called with a potential error object as the first parameter and the matching files as the second.

How do I read a text file line by line in node JS?

Method 1: Using the Readline Module: Readline is a native module of Node. js, it was developed specifically for reading the content line by line from any readable stream. It can be used to read data from the command line. const readline = require('readline');


1 Answers

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 fsReaddir = util.promisify(fs.readdir);
const fsReadFile = util.promisify(fs.readFile);
const fsLstat = util.promisify(fs.lstat);

async function searchFilesInDirectoryAsync(dir, filter, ext) {  
    const files = await fsReaddir(dir).catch(err => {
        throw new Error(err.message);
    });
    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.

like image 160
Kamil Solecki Avatar answered Oct 21 '22 03:10

Kamil Solecki