commanderjs
A complete solution for node.js command-line interfaces, inspired by Ruby's commander.
Installation
$ npm install commander
Declare program variable
Commander exports a global object for easy and fast programming. For brevity, examples in this README use it.
const program = require('commander');
program.version('0.0.1');
For larger programs that may use commander in many ways, including unit testing, it is better to create a local Command object to use.
const commander = require('commander');
const program = new commander.Command();
program.version('0.0.1');
Options
The .option()
method is used to define commander with options, and also for documenting these options. Each option can have a short flag (single character) and a long name, separated by a comma or space.
Options are placed on the Commander object's properties, multi-word options like --template-engine are converted to camelCase program.templateEngine
. Multiple short flags can be combined into one argument, such as -a -b -c
is equivalent to -abc
.
Common option types, boolean and value
The two most common types of options are boolean (no value after the option) and options with a value (declared with angle brackets). Both are undefined
unless specified in the command line.
const program = require('commander');
program
.option('-d, --debug', 'output extra debugging')
.option('-s, --small', 'small pizza size')
.option('-p, --pizza-type <type>', 'flavour of pizza');
program.parse(process.argv);
if (program.debug) console.log(program.opts());
console.log('pizza details:');
if (program.small) console.log('- small pizza size');
if (program.pizzaType) console.log(`- ${program.pizzaType}`);
$ pizza-options -d
{ debug: true, small: undefined, pizzaType: undefined }
pizza details:
$ pizza-options -p
error: option `-p, --pizza-type <type>' argument missing
$ pizza-options -ds -p vegetarian
{ debug: true, small: true, pizzaType: 'vegetarian' }
pizza details:
- small pizza size
- vegetarian
$ pizza-options --pizza-type=cheese
pizza details:
- cheese
program.parse(arguments)
processes the arguments, unused options are stored in the program.args
array.
Default option values
You can set a default value for an option.
const program = require('commander');
program.option('-c, --cheese <type>', 'add the specified type of cheese', 'blue');
program.parse(process.argv);
console.log(`cheese: ${program.cheese}`);
$ pizza-options
cheese: blue
$ pizza-options --cheese stilton
cheese: stilton
Other option types, ignorable booleans and flag values
When the option value is of type boolean, you can add no-
to the long name to make the default value true, and if the option is passed, the value is false.
const program = require('commander');
program.option('-n, --no-sauce', 'Remove sauce').parse(process.argv);
if (program.sauce) console.log('you ordered a pizza with sauce');
else console.log('you ordered a pizza without sauce');
$ pizza-options
you ordered a pizza with sauce
$ pizza-options --sauce
error: unknown option `--sauce'
$ pizza-options --no-sauce
you ordered a pizza without sauce
You can specify an option as a flag that accepts a value (declared with square brackets, i.e., passing a value is not required).
const program = require('commander');
program.option('-c, --cheese [type]', 'Add cheese with optional type');
program.parse(process.argv);
if (program.cheese === undefined) console.log('no cheese');
else if (program.cheese === true) console.log('add cheese');
else console.log(`add cheese type ${program.cheese}`);
Custom option processing
You can specify a function to process the value of an option, which takes two arguments: the value passed by the user and the previous value, and it returns a new option value.
You can cast the option value to the required type, accumulate values, or completely customize the processing.
You can specify the default or initial value of the option after the function.
const program = require('commander');
function myParseInt(value, dummyPrevious) {
// parseInt takes a string and an optional radix
return parseInt(value);
}
function increaseVerbosity(dummyValue, previous) {
return previous + 1;
}
function collect(value, previous) {
return previous.concat([value]);
}
function commaSeparatedList(value, dummyPrevious) {
return value.split(',');
}
program
.option('-f, --float <number>', 'float argument', parseFloat)
.option('-i, --integer <number>', 'integer argument', myParseInt)
.option('-v, --verbose', 'verbosity that can be increased', increaseVerbosity, 0)
.option('-c, --collect <value>', 'repeatable value', collect, [])
.option('-l, --list <items>', 'comma separated list', commaSeparatedList);
program.parse(process.argv);
if (program.float !== undefined) console.log(`float: ${program.float}`);
if (program.integer !== undefined) console.log(`integer: ${program.integer}`);
if (program.verbose > 0) console.log(`verbosity: ${program.verbose}`);
if (program.collect.length > 0) console.log(program.collect);
if (program.list !== undefined) console.log(program.list);
$ custom -f 1e2
float: 100
$ custom --integer 2
integer: 2
$ custom -v -v -v
verbose: 3
$ custom -c a -c b -c c
[ 'a', 'b', 'c' ]
$ custom --list x,y,z
[ 'x', 'y', 'z' ]
Version option
The version
method processes the display version command, the default option flags are -V
and --version
, and the version number is printed and exited when present.
program.version('0.0.1');
$ ./examples/pizza -V
0.0.1
You can customize the flags by passing another argument to the version
method, with the syntax similar to the option
method. The version flag name can be arbitrary, but it must have a long name.
program.version('0.0.1', '-v, --version');
Specify command options
You can bind options to commands.
#!/usr/bin/env node
var program = require('commander');
program
.command('rm <dir>')
.option('-r, --recursive', 'Remove recursively')
.action(function (dir, cmd) {
console.log('remove ' + dir + (cmd.recursive ? ' recursively' : ''));
});
program.parse(process.argv);
When using this command, the options of the command will be verified. Any unknown options will be reported as an error. However, if the command based on the operation does not define an action, the options are not verified.
Regular expressions
program
.version('0.1.0')
.option('-s --size <size>', 'Pizza size', /^(large|medium|small)$/i, 'medium')
.option('-d --drink [drink]', 'Drink', /^(coke|pepsi|izze)$/i)
.parse(process.argv);
console.log(' size: %j', program.size);
console.log(' drink: %j', program.drink);
Note: If the value passed for the size option does not match the regular expression, the value is medium (default value). If the drink option does not match the regular expression, the value is true.
Variable number of arguments
The last argument of a command can be a variable number of arguments, and only the last argument can be variable. To make an argument variable, you need to append ...
after the argument name. Here is an example:
#!/usr/bin/env node
/**
* Module dependencies.
*/
var program = require('commander');
program
.version('0.0.1')
.command('rmdir <dir> [otherDirs...]')
.action(function (dir, otherDirs) {
console.log('rmdir %s', dir);
if (otherDirs) {
otherDirs.forEach(function (oDir) {
console.log('rmdir %s', oDir);
});
}
});
program.parse(process.argv);
The values of variable arguments are saved in the form of an array
. As shown above, this is true for the arguments passed to your action as well as the values in program.args
.
Syntax for specifying arguments
#!/usr/bin/env node
var program = require('../');
program
.version('0.0.1')
.arguments('<cmd> [env]')
.action(function (cmd, env) {
cmdValue = cmd;
envValue = env;
});
program.parse(process.argv);
if (typeof cmdValue === 'undefined') {
console.error('no command given!');
process.exit(1);
}
console.log('command:', cmdValue);
console.log('environment:', envValue || 'no environment given');
Angle brackets (e.g., <cmd>
) represent required inputs, and square brackets (e.g., [env]
) represent optional inputs.
Git-style subcommands
// file: ./examples/pm
var program = require('..');
program
.version('0.0.1')
.command('install [name]', 'install one or more packages')
.command('search [query]', 'search with optional query')
.command('list', 'list packages installed', { isDefault: true })
.parse(process.argv);
When .command()
has a description argument, you cannot use .action(callback)
to handle subcommands, otherwise, it will cause an error. This tells commander that you will use separate executable files as subcommands, just like git(1)
and other popular tools.
Commander will attempt to search for program-command
style executable files in the directory of the entry script (e.g., ./examples/pm
), such as pm-install
, pm-search
.
You can pass options when calling .command()
. Setting opts.noHelp
to true
will exclude the option from the generated help output. Setting opts.isDefault
to true
will execute the subcommand when no other subcommand is specified.
If you plan to install the command globally, make sure the executable files have the corresponding permissions, such as 755
.
--harmony
You can enable --harmony
in two ways:
- Add
#!/usr/bin/env node --harmony
to the subcommand script. Note that some system versions do not support this mode. - Add the
--harmony
argument when calling the command, for example,node --harmony examples/pm publish --harmony
the option will be retained when spawning child processes.
Automated help information --help
Help information is automatically generated by commander based on your program, and the following is the help information generated by --help
:
$ ./examples/pizza --help
Usage: pizza [options]
An application for pizzas ordering
Options:
-h, --help output usage information
-V, --version output the version number
-p, --peppers Add peppers
-P, --pineapple Add pineapple
-b, --bbq Add bbq sauce
-c, --cheese <type> Add the specified type of cheese [marble]
-C, --no-cheese You do not want any cheese
Custom help
You can control what -h, --help
displays by listening to --help
. Once invoked, Commander will automatically exit, and the rest of your program will not be displayed. For example, the "stuff" below will not be output when --help
is executed.
#!/usr/bin/env node
/**
* Module dependencies.
*/
var program = require('commander');
program.version('0.0.1').option('-f, --foo', 'enable some foo').option('-b, --bar', 'enable some bar').option('-B, --baz', 'enable some baz');
// must be before .parse() since
// node's emit() is immediate
program.on('--help', function () {
console.log('');
console.log('Examples:');
console.log(' $ custom-help --help');
console.log(' $ custom-help -h');
});
program.parse(process.argv);
console.log('stuff');
The following help information is output when running node script-name.js -h
or node script-name.js --help
:
Usage: custom-help [options]
Options:
-h, --help output usage information
-V, --version output the version number
-f, --foo enable some foo
-b, --bar enable some bar
-B, --baz enable some baz
Examples:
$ custom-help --help
$ custom-help -h
.outputHelp(cb)
Outputs help information without exiting. An optional callback can be processed after displaying the help text. If you want to display the default help (for example, if no command is provided), you can use something like this:
var program = require('commander');
var colors = require('colors');
program.version('0.0.1').command('getstream [url]', 'get stream URL').parse(process.argv);
if (!process.argv.slice(2).length) {
program.outputHelp(make_red);
}
function make_red(txt) {
return colors.red(txt); // 在控制台上显示红色的帮助文本
}
.help(cb)
Outputs help information and exits immediately. An optional callback can be processed after displaying the help text.
Custom event listening
You can perform custom functions by listening to commands and options.
// 当有选项verbose时会执行函数
program.on('option:verbose', function () {
process.env.VERBOSE = this.verbose;
});
// 未知命令会报错
program.on('command:*', function () {
console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args.join(' '));
process.exit(1);
});
Miscellaneous knowledge
TypeScript
The package includes TypeScript definition files, but you need to install node types yourself. For example:
npm install commander
npm install --save-dev @types/node
If you are using ts-node
and git-style subcommands to write .ts
files, you need to execute the program with node to ensure that subcommands are executed correctly. For example:
node -r ts-node/register pm.ts
Examples
var program = require('commander');
program
.version('0.0.1')
.option('-C, --chdir <path>', 'change the working directory')
.option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
.option('-T, --no-tests', 'ignore test hook');
program
.command('setup [env]')
.description('run setup commands for all envs')
.option('-s, --setup_mode [mode]', 'Which setup mode to use')
.action(function (env, options) {
var mode = options.setup_mode || 'normal';
env = env || 'all';
console.log('setup for %s env(s) with %s mode', env, mode);
});
program
.command('exec <cmd>')
.alias('ex')
.description('execute the given remote cmd')
.option('-e, --exec_mode <mode>', 'Which exec mode to use')
.action(function (cmd, options) {
console.log('exec "%s" using %s mode', cmd, options.exec_mode);
})
.on('--help', function () {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ deploy exec sequential');
console.log(' $ deploy exec async');
});
program.command('*').action(function (env) {
console.log('deploying "%s"', env);
});
program.parse(process.argv);