Write your shell scripts in JavaScript, via Node.js

[2011-12-16] jsshell, dev, nodejs, javascript, shell
(Ad, please don’t block)
Update 2012-08-21: All posts about shell scripting via Node.js have the label “jsshell”.

Do you know JavaScript and want to write a shell script? Then you should give Node.js a try. It is easy to install and shell scripts are a great way to get to know it. This post explains the basics.

Accessing arguments

You access command line arguments via process.argv, an array with the following contents:
    [ nodeBinary, script, arg0, arg1, ... ]
Thus, the first argument is at process.argv[2] and you can loop over all arguments via:
    process.argv.slice(2).forEach(function (fileName) {
        ...
    });
If you want to do more sophisticated argument processing, you can take a look at the Node.js modules nomnom and optimist. From now on, we’ll frequently use the file system module:
    var fs = require('fs');

Reading a text file

If your file isn’t too large, you can read it in one go, into a string:
    var text = fs.readFileSync(fileName, "utf8");
Afterwards, you can split the text, to process the content line by line.
    text.split(/\r?\n/).forEach(function (line) {
        // ...
    });
For larger files, you can work with a stream to iterate over the lines. mtomis describes one solution at Stack Overflow.

Writing a text file

You can write the complete contents to a file, via a string.
    fs.writeFileSync(fileName, str, 'utf8');
Or you can incrementally write strings to a stream.
    var out = fs.createWriteStream(fileName, { encoding: "utf8" });
    out.write(str);
    out.end(); // currently the same as destroy() and destroySoon()

Cross-platform considerations

Determining the line break string.

Solution 1: Read an existing file into a string, search for a line break, assume the line break is "\n" if you can’t find one.
    var EOL = fileContents.indexOf("\r\n") >= 0 ? "\r\n" : "\n";
Solution 2: Check the platform. All Windows platforms return "win32", even 64bit ones.
    var EOL = (process.platform === 'win32' ? '\r\n' : '\n')

Working with paths

Use module path when working with file system paths. That ensures that the right path segment separator is used ("/" on Unix, "\" on Windows).
    var path = require('path');
    path.join(mydir, "foo");

Running the script

If your shell script is called myscript.js, you run it as follows:
    node myscript.js arg1 arg2 ...
On Unix, you have the option to prepend a line telling the operating system how to run the script:
    #!/usr/bin/env node
Then you only need to make the script executable:
    chmod u+x myscript.js
Now it can be run on its own:
    ./myscript.js arg1 arg2 ...

Other topics

Related reading

  1. Tip: load source from a file in the Node.js shell
  2. Execute code each time the Node.js REPL starts