Command-line scripting with Javascript

December 19, 2008

The short version:

Bash users, preface your standalone javascripts with:
//usr/bin/env js -e 'var __main__ = true;' $0 $*; exit

The long version:

To make standalone javascript files that are compatible with both bash and javascript’s load() function, you need to be clever.

I’ve been using the SpiderMonkey javascript engine for command-line javascript as I play around with JavaScript: The Good Parts. Instead of fooling around with a web browser, I put code into text files and run them on the command-line.

Aside: Bash

I use bash as my shell. 

When bash loads a file, if that file starts with something like


<rest of file>

then bash loads the interpreter and passes it <rest of file>.

So, I make a file like

#!/usr/bin/env js

print("Hello world.");

and the command js sees print("Hello world."); and does its thing.

Otherwise, bash just treats the file as a bash script and interprets it with its own rules.

Aside: load()

In SpiderMonkey, the load() function takes a string parameter. The string represents a path to a file, and SpiderMonkey reads and executes the contents of the file in the context of the loader.

There’s a problem: If a file starts with #!/usr/bin/env js, load sputters out a SyntaxError, because that string isn’t valid JavaScript. We can’t use the #! syntax.

The Solution

We want a way of structuring our javascript code such that:

  1. If interpreted by bash, bash loads js and runs the file.
  2. If loaded by SpiderMonkey’s load, the JavaScript code is loaded into the loader.
  3. The file javascript has some way of knowing if it has been loaded or run from the command-line.

In several other scripting languages such as Ruby, Perl and Python, ‘#‘ is the comment character so there’s no problem with the #! line.

In JavaScript, we can ignore things using //.

/ is the path separator on Unix systems.

So, bash will interpret the string //usr/bin/env js as a command to run, and js will ignore it.

Next, we have a little bit more magic. js expects a file name, and it can also take arguments to pass in to the JavaScript (which will be stored in the global variable arguments).

In bash syntax, $0 represents the name of the file being invoked, and $* represents the arguments.

So, //usr/bin/env js $0 $* runs js on the current file, passing it the arguments that were passed to the script.

We don’t want bash to try to run the JavaScript code, so we attach ; exit  to the command to cause it to stop executing the file.

Thus, the magic incantation //usr/bin/env js $0 $*; exit allows a JavaScript file to be executed on the command line and loaded from other JavaScript files.

So, we can make a file like:

//usr/bin/env js $0 $*; exit

for(var i in arguments) {

and save it as args.js and run it like so:

js-play aran$ ./args.js Hello World.
js-play aran$ 

We can also make another file like:

//usr/bin/env js $0 $*; exit


and save it as argsloader.js and run it like so:

js-play aran$ chmod +x argsloader.js
js-play aran$ ./argsloader.js Hello World.

Now, for a finishing touch, we want our file to know if it has been loaded by bash or by another javascript. For this, we take advantage of SpiderMonkey’s -e option, which runs some JavaScript code before running a file.

We can use -e to define a variable. Then our script can check for the presence or absence of that variable definition and thus decide.

//usr/bin/env js -e 'var __main__ = true;' $0 $*; exit

if(typeof __main__ !== 'undefined') {
    print("I'm a standalone program.")
} else {
    print("I was loaded.");


js-play aran$ ./argsPlay.js 
I'm a standalone program.
js-play aran$ js -e 'load("argsPlay.js");'
I was loaded.
js-play aran$ 

And so, our final magic incantation is //usr/bin/env js -e 'var __main__ = true;' $0 $*; exit and it is good.


One Response to “Command-line scripting with Javascript”

  1. […] load() for Command-line JavaScript December 20, 2008 In a previous post on command-line JavaScript, I gave a trick for making JavaScript files you can load from the command-line as well as from […]

Comments are closed.

%d bloggers like this: