If JavaScript is your language of choice, then you’ve probably written your fair share of command-line scripts that run on Node.js. Oftentimes you can get away with writing simple one-off scripts that only serve a single purpose. Other times you may want things to be more customizable and flexible and that’s where being able to pass-in arguments comes in.
Argument Vector
Node.js makes it easy to get the list of passed arguments, known as an argument vector. The argument vector is an array available from process.argv
in your Node.js script.
The array contains everything that’s passed to the script, including the Node.js executable and the path/filename of the script. If you were to run the following command:
Your argument vector would contain five items:
[ '/usr/bin/node', '/some/path/to/example.js', '-a', '-b', '-c' ]
At the very least, a script that’s run without any arguments will still contain two items in the array, the node
executable and the script file that’s being run.
Typically the argument vector is paired with an argument count (argc
) that tells you how many arguments have been passed-in. Node.js lacks this particular variable but we can always grab the length of the argument vector array:
if (process.argv.length === 2) { console.error('Expected at least one argument!'); process.exit(1); }
Simple Argument Flags
The simplest way to implement arguments into your script would be to implement just a single flag that could be passed-in and modify the output of your script:
if (process.argv[2] && process.argv[2] === '-c') { console.log('After \'while, Crocodile!'); } else { console.log('See ya later, Alligator!'); }
This script checks if we have a third item in our argument vector (index 2
because the array index in JavaScript is zero-based) and if it’s present and is equal to -c
it will alter the output:
node reptile-salutation.js # See ya later... node reptile-salutation.js -c # After 'while...
We don’t have to limit ourselves to just modifying the conditional control structure, we can use the actual value that’s been passed to the script as well:
const name = (process.argv[2] || 'human'); console.log(`Alligators are the best! Don't you agree, ${name}?`);
Instead of a conditional based on the argument, this script takes the value that’s passed in (defaulting to “human” when the argument is missing) and injects it into our script’s output.
Arguments with Values
We’ve written a script that accepts an argument and one that accepts a raw value, what about in scenarios where we want to use a value in conjunction with an argument?
To make things a bit more complex, let’s also accept multiple arguments:
// Checks to see if the -c argument is present const reptile = ( process.argv.indexOf('-c') > -1 ? 'Crocodiles' : 'Alligators' ); // Also checks for --name and if we have a value const nameIndex = process.argv.indexOf('--name'); let nameValue; if (nameIndex > -1) { // Grabs the value after --name nameValue = process.argv[nameIndex + 1]; } const name = (nameValue || 'human'); console.log(`${reptile} are the best! Don't you agree, ${name}?`);
By using indexOf
instead of relying on specific index values, we are able to look for the arguments anywhere in the argument vector, regardless of the order!
commander
-in-chief
All of this vanilla JavaScript has been well and good, but command-line arguments can get complex pretty quickly.
The aforementioned examples work when the argument input is quite specific but out in the wild you could pass in arguments with and without equal signs (-nJaneDoe
or --name=JohnDoe
), quoted strings to pass-in values with spaces (-n "Jane Doe"
) and even have arguments aliased to provide short and longhand versions.
That’s where the [commander](https://github.com/tj/commander.js)
library can lend a helpful hand.
commander
is a popular Node.js library that is inspired by the Ruby library of the same name.
To use commander
, you need to install the dependency to your project with npm
or yarn
:
# via npm $ npm install commander --save # via yarn $ yarn add commander
Once installed, you can require
it and start kicking some command-line butt!
Let’s take our previous example and port it to use commander
and expand on the arguments a bit:
const commander = require('commander'); commander .version('1.0.0', '-v, --version') .usage('[OPTIONS]...') .option('-c, --crocodile', 'Use crocodile instead of alligator') .option('-n, --name <name>', 'Your name', 'human') .parse(process.argv); const reptile = (commander.crocodile ? 'Crocodiles' : 'Alligators'); console.log( `${reptile} are the best! Don't you agree, ${commander.name}?` );
commander
does all of the hard work by processing process.argv
and adding the arguments and any associated values as properties in our commander
object.
Going this route comes with some added bonuses as well.
We can easily version our script and report the version number with -v
or --version
. We also get some friendly output that explains the script’s usage by passing the --help
argument and if you happen to pass an argument that’s not defined or is missing a passed value, it will throw an error.
Conclusion
Regardless if you roll your own logic to handle arguments or leverage a third-party library like commander
, command-line arguments are a great way to super charge your command-line Node.js scripts.
If you are planning on writing a script that you are planning to release to the world, I would recommend going with commander
or an equivalent library over rolling your own logic due to it’s robustness and ability to handle just about anything you can throw at it.