Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / Node.js

Linux's [wc] Command Cloned with node.js

5.00/5 (1 vote)
26 Jul 2013CPOL2 min read 8.6K  
This time I implemented the wc command from Linux using node.js

Introduction

I continued the implementation of Linux commands with the help of node.js. This time, I've chosen the wc command. You can see the code below, it is a really small program. It does not have all the features which the original command has, it can do 3 basic things: counting the newlines, counting the words and counting the characters from a given file.

JavaScript
#!/usr/bin/env node

/*

    This small node.js app should do exactly what the wc linux command does.
    
    Quote from man wc:
         Print  newline,  word,  and  byte counts for each FILE, and a total line if more than one FILE is specified.  With no FILE, or when FILE is -, read standard
       input.  A word is a non-zero-length sequence of characters delimited by white space.  The options below may be used to  select  which  counts  are  printed,
       always in the following order: newline, word, character, byte, maximum line length.
       
   
     In this version, I will use the Commander node js extension, please see the https://github.com/visionmedia/commander.js github page.
     Please use the npm install commander command to install the library in your node app folder.

*/

var fs = require('fs');
var commander = require('commander');

var NEW_LINE_DELIMITER = '\n';
var SPACE = ' ';
var TAB = '\t';

var cloneFunction = function(fn) {
    // Workaround for commander.js issue.
    // http://stackoverflow.com/a/6772648
    return fn.bind({});
};

var checkIfFileExists = function(infile) {
    var infilePath = infile.toString();
    var stats = fs.statSync(infilePath);
    if(!stats && !stats.isFile()) {
        process.stdout.write('%s does not exist.', infilePath);                
        process.exit(1);
    }
    return infilePath;
};

var countLines = function(infile) {
    var fileContent = fs.readFileSync(infile);
    if(fileContent) {
        var lineCount = fileContent.toString().split(NEW_LINE_DELIMITER);
        if(lineCount && lineCount.length > 0) {
            var numberOfLines = lineCount.length - 1;
            process.stdout.write(numberOfLines + ' ' + infile +  NEW_LINE_DELIMITER);
        }
        else {
            process.stdout.write('The file is empty.');
        }
    }
    else {
        process.stdout.write('The file is empty.');
    }
};

var countWords = function(infile) {
    var fileContent = fs.readFileSync(infile);
    if(fileContent) {
        var sumOfWords = 0;
        var lines = fileContent.toString().split(NEW_LINE_DELIMITER);
        for(var i=0; i<lines.length ; ++i) {
            if(lines[i] != NEW_LINE_DELIMITER && lines[i] != TAB && lines[i].length > 0) {
                sumOfWords += (lines[i].split(SPACE)).length;            
            }
        }        
        process.stdout.write(sumOfWords + ' ' + infile +  NEW_LINE_DELIMITER);
    }
    else {
        process.stdout.write('The file is empty.');
    }
};

var countCharacters = function(infile) {
    var fileContent = fs.readFileSync(infile);
    if(fileContent) {
        var textInFile = fileContent.toString();
        process.stdout.write(textInFile.length + ' ' + infile +  NEW_LINE_DELIMITER);
    }
    else {
        process.stdout.write('The file is empty.');
    }
};

  commander.option('-l, --lines <file>', 'Path to file to analyze', cloneFunction(checkIfFileExists))
           .option('-w, --words <file>', 'Path to file to analyze', cloneFunction(checkIfFileExists))
           .option('-c, --characters <file>', 'Path to file to analyze', cloneFunction(checkIfFileExists))
           .parse(process.argv);
        
            if(commander.lines) {
                countLines(commander.lines);
            }
            
            if(commander.words) {
                countWords(commander.words);
            }
            
            if(commander.characters) {
                countCharacters(commander.characters);
            }         

There is a method which I would like to explain more in depth: checkIfFileExists. Here it is used in the statsync method of the node.js file system module. The statsync method returns information(stats) about a file; these can be queried with the help of the following methods:

JavaScript
//
// Method names taken from: http://nodejs.org/api/fs.html#fs_class_fs_stats
//

//stats is the return value if fs.statsync(PATH_TO_FILE) method
stats.isFile();
stats.isDirectory();
stats.isBlockDevice();
stats.isCharacterDevice();
stats.isSymbolicLink(); //(only valid with fs.lstat())
stats.isFIFO();
stats.isSocket(); 

The code of the checkFileExists method raises an error if there was no input passed to it, or the given path is not pointing to a file.

The countLines function logic is simple, it reads the content of the file, and splits the string read by the new line delimiter (under Linux, this is the "\n").

The countWords function does almost the same thing as the countLines, the only difference is, after splitting the content of the file to lines with the new line delimiter, the lines' content is also split using the ' ' [SPACE] character as splitting value. After having the content of the file split based on new lines and spaces, it is easy to sum up the number of words.

The countCharacters method is the most simple one, it reads the content of the file and returns the length of the string.

For splitting up and working with command line arguments, I use a node js module called commander. This can be installed very easily with the classic npm install command.

I was testing the code with few files and it seems to work ok, it gives the same results as the original wc command. The implementation is not 100% yet, but I will try to improve it; or you can fork my github repo with these Linux command clones from the linjs repository and add some features.

Thanks for reading.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)