Handling Command-line Arguments in Node.js Scripts

joshtronic

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:

node example.js -a -b -c

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.

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#