Accepting Input via Stdin and Arguments in a Command Line Node.js Script
There are many cases in which it is either necessary or simply more elegant for a script to accept input either as arguments or piped to stdin. To have any of these following options for the same script in other words:
# Via arguments. ./example-script "pass in this string as input" # Via pipe. echo "pass in this string as input" | ./example-script # Via pipe from a file, better for larger strings. echo "pass in this string as input" > input.txt cat input.txt | ./example-script
Writing a standalone script in Node.js is simple. Create a single Javascript file that specifies its environment as Node.js on the first line, give it executable permissions, and it can then be executed directly as in the example above.
#!/usr/bin/env node /** * @fileOverview An example command line script. */ // // Standard Node.js Javascript goes here. //
The one and only key piece of knowledge here is how to interrogate the process instance to determine whether or not the script is being sent piped data. This isn't at all easy to find in the Node.js documentation. Fortunately we live in the brave new world of ubiquitous global communication, so every trivial challenge is solved and the solution broadcast long before latecomers such as I encounter it. Here is a short proof of concept script that outputs the length of the content passed to it, either via stdin or arguments:
#!/usr/bin/env node /** * @fileOverview An example command line script. * * This outputs the number of bytes the content passed to it. */ var encoding = 'utf-8'; var data; /** * Once we have data, carry out processing. In this case * that means writing the length of the input in bytes. */ function processData () { console.info(Buffer.byteLength(data, encoding)); } // ------------------------------------------------------------ // Called with arguments. E.g.: // ./example-script "pass in this string as input" // ------------------------------------------------------------ if (process.stdin.isTTY) { // Even though executed by name, the first argument is still "node", // the second the script name. The third is the string we want. data = new Buffer(process.argv[2] || '', encoding); processData(); } // ------------------------------------------------------------ // Accepting piped content. E.g.: // echo "pass in this string as input" | ./example-script // ------------------------------------------------------------ else { data = ''; process.stdin.setEncoding(encoding); process.stdin.on('readable', function() { var chunk; while (chunk = process.stdin.read()) { data += chunk; } }); process.stdin.on('end', function () { // There will be a trailing \n from the user hitting enter. Get rid of it. data = data.replace(/\n$/, ''); processData(); }); }
Of course you can do a lot more in either case, such as waiting for and responding to user input dynamically in TTY mode, and streaming data in piped mode rather than gathering it up to use all at once. The point of this exercise was only to record the use of process.stdin.isTTY to identify how command line input is expected to arrive.