January 21, 2014

Cross-platform Unix shell scripting with shell.js

In my daily development, I rely on bash religiously. I use git shell for git operations, and my build systems rely on node. A headache that comes with this is writing cross-platform scripts (one example is the headaches with backslashes vs forwardslashes).

Previously, I relied on separate scripts per OS, batch (.bat) for Windows, and Bash (.sh) for Linux and Mac. This was getting out of control, as keeping these in sync had become a hassle.

For more complex builds I use Grunt, but sometimes I just need to execute a system command, and copy a few files, and deploying Grunt for that seems to be an over-kill.

Shell.js to the rescue

Get it on GitHub

ShellJS provides a set of Unix shell commands that work cross platform. And you write them in JavaScript... How awesome is that?

If you are familiar with Makefiles, you know that they provide a set of commands that tell the compiler system what to do with your code. This includes different targets, for example, minifying the code in production versus development mode.

ShellJS allows writing Makefiles that do just that. This also comes handy when working on Apache Cordova projects.

Let's start with a more basic example:

//file: make.js
require("shelljs/make");

//prepare project
target.prepare = function() {
	if (exec("cordova prepare blackberry10").code != 0) {
		exit(1);
	}
};

//run project
target.run = function() {
	if (exec("cordova run blackberry10 --devicepass pass").code != 0) {
		exit(1);
	}
};

target.name provides targets that you can run from commandline. To execute the makefile, I run:

node make prepare //prepares project

or:

node make run //runs project

You can also add additional targets for other platforms, for example:

node make android

Also, you can add the following line, to provide a default target:

target.all = target.prepare;

This is executed when running node make alone.

Some shellJS commands I use often:

  1. cp(from,to) - cross platform copy
  2. path.join(paths...) allows to join paths, without worrying about target platform
  3. which(command) - allows to check if a specific command (for example "node") exists on the system
  4. exec(command) - executes command and returns exec code (for error checking)
  5. echo(string) - outputs a string

For more usage examples, check out GitHub