Wednesday, August 26, 2015

cat in Node


I have a longstanding interest in writing system tools in different languages. In this post, I'll look at implementing a simple cat written in JavaScript and executed on Node.

The implementation is pretty succinct:


#! /usr/bin/env node --harmony

const
  fs = require('fs')
, readline = require('readline')
, paths = process.argv.slice(2)
, cat = function(path) {

    var stm = path ? fs.createReadStream(path) : process.stdin;

    readline.createInterface({
      input: stm,
      output: process.stdout
    }).on('line', function(line) {

      process.stdout.write(line + "\n");
    });
  }
;

if(0 == paths.length) {

  cat(null);
} else {

  // process each one at a time
  paths.forEach(function(path) {

    cat(path);
  });
}

We start with requiring two standard components: fs and readline (which I won't explain further here, because they're standard, so you probably should know them, and because the docs are great, so you can get to know them easily if you need to). Both of these are not going to be reassigned, so I define them const.

Also defined in this way is the paths variable, which obtains all the arguments given on the command-line (excepting the script name, of course), and a cat() function, which takes a single parameter, path. If path is null, cat() uses the standard input stream (process.stdin()); if not, a stream is created on the file (via fs.createReadStream()). (Naturally, if the file does not exist or cannot be accessed for reading by the calling process, an exception will be thrown.)

Inside cat() we invoke readline.createInterface(), specifying our discriminated stream for input and standard output stream for output, and then specify a handler for the 'line' event that writes out the line.

With cat() so defined, we invoke according to the number of arguments we're given. If we're given one or more arguments, then invoke cat() for each; otherwise we pass null to read from standard input.

Note that this may have a concurrency vulnerability, insofar as the contents from multiple paths may be interleaved when the fs components' buffering limit is exceeded, due to Node's concurrency model, but I have yet to experience it in testing. Just don't treat this as production code.

No comments:

Post a Comment