Writing Command Line Tools with Node
Back in August 2012 I wrote a post on building a command line tool in NodeJS. That post is now over two years old and plenty has changed, hence I thought it worth writing a new post building the same tool, showing how I'd do it now.
We're going to build the same tool, one that's used to search a directory for files that match a given string. This is not a very useful plugin, but will let me demonstrate the basics of building a CLI in NodeJS.
Creating the Project
First things first: let's create a new project. Create a directory for the project, enter it and run npm init
to initialise the new project with a package.json
file. Answer the prompts if you wish, or just hit enter a bunch of times to get a template package.json
file that you can fill out at your own leisure.
Editing package.json
The package.json
file is used by npm, Node's package manager, to know about your project, its dependencies and how it works. We need to make a couple of edits to it.
- remove the
main
entry: this is only used for modules that will be used through the module system (e.g.var _ = require('underscore');
). - add
preferGlobal
and set it to true, which means if someone installs this module through npm and doesn't use the--global
option, they will be warned that the module is designed to be installed globally. - add the
bin
object, which maps commands to files. This means when this module is installed, npm will set up thefilesearch
executable to executeindex.js
.
{
"name": "filesearch",
"version": "1.0.0",
"description": "searches for files",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "JavaScript Playground",
"license": "ISC",
"preferGlobal": true,
"bin": {
"filesearch": "index.js"
}
}
Creating the Script
Create index.js
and add this to the top:
#! /usr/bin/env node
console.log('This is the filesearch script.');
Installing the Script
Now in your project you can run npm link
to install the script on your system. This creates a symlink to your project so that you can run the project whilst working on it, with no need to keep reinstalling it over and over again.
Once npm link
has run, you should be able to run filesearch
on the command line and see the string printed back:
~/git/filesearch > filesearch
This is the filesearch script.
Processing Arguments
filesearch
is going to be called with one argument, which is going to be the pattern to search through files for. We need to get at that argument. When a Node.js script is executed on the command line, the process.argv
array contains all the arguments used to call that script.
Change index.js
so it instead logs out this array:
console.log(process.argv);
And now run the script again, this time with an argument:
~/git/filesearch > filesearch foo
[ 'node', '/Users/jackfranklin/.nvm/v0.10.32/bin/filesearch', 'foo']
The first argument is always node
, and the second is the path to the file that has been executed. Any following arguments are ones that the user has called your script with, and those are the ones we care about. We can use slice
to get an array of just the arguments we need:
var userArgs = process.argv.slice(2);
var searchPattern = userArgs[0];
Now we have the one argument we need.
Searching for Files
We'll hand the actual searching of the files over to a combination of two Unix commands, ls
and grep
. We can use ls -a
to list all files in the current directory, and pass them to grep
to search for our actual pattern.
To run a command in the system we can use the exec
method of the child_process
module - a module that comes with Node and doesn't need to be separately installed - to execute the right command, passing in the search pattern the user passed in through to grep
:
var exec = require('child_process').exec;
var child = exec('ls -a | grep ' + searchPattern, function(
err,
stdout,
stderr
) {
console.log(stdout);
});
And that is that! We can now run filesearch
and see the results:
~/git/filesearch > filesearch package
package.json
Next Steps
If this was a real module that I was working on publishing there's a couple of things I'd do before hitting npm publish
:
- ensure a good, well written README
- decide on an initial version number (I tend to go for
0.1.0
) and then follow semver
When your module is ready, simply run npm publish
to push it onto npm. If you've not registered on npm, you can run npm adduser
and follow the prompts to set up and authenticate yourself.
Once published, users can then install your module using npm install --global filesearch
.