commander.js是完整的 node.js 命令行解决方案。我们用它来开发命令行程序。
安装
npm install commander
编写代码来描述你的命令行界面。 Commander 负责将参数解析为选项和命令参数,为问题显示使用错误,并实现一个有帮助的系统。
选项
Commander 使用.option()
方法来定义选项,同时可以附加选项的简介。每个选项可以定义一个短选项名称(-后面接单个字符)和一个长选项名称(--后面接一个或多个单词),使用逗号、空格或|
分隔。
常用选项类型,boolean 型选项和带参数选项
有两种最常用的选项,一类是 boolean 型选项,选项无需配置参数,另一类选项则可以设置参数(使用尖括号声明在该选项后,如--expect <value>
)。
示例: index.js
const { program } = require('commander');
program
.option('--first', '这是一个长选项名称')
.option('-s, --separator <char>', '这是一个有短选项名称和长选项名称,并且带了参数');
program.parse();
const options = program.opts();
console.log('options', options);
console.log('program.args', program.args);
运行:
node index --first
// options { first: true } program.args []
node index --first xxx xx
// options { first: true } program.args ['xxx', 'xx']
node index -s type xx
// options { separator: 'type' } program.args ['xx']
node index --separator type xx
// options { separator: 'type' } program.args ['xx']
上面的例子中,options选项分为 --first
, -s
或者--separator
它带了一个参数,如果运行它不加上参数就会报错。从运行结果来看,不带参数的 first
的options选项对象值是布尔值,带参数的 --separator
的Options选项对象值是它的参数。
program.args
的值是 没有被使用的选项
调用方式可以如下
node index -s 80
node index -s80
node index --separator 80
node index --separator=80
多个布尔短选项可以在破折号之后组合在一起,并且可以跟一个取值的单一选项。
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);
const options = program.opts();
if (options.debug) console.log(options);
console.log('pizza details:');
if (options.small) console.log('- small pizza size');
if (options.pizzaType) console.log(`- ${options.pizzaType}`);
上面的例子可以这样调用: -d -s -p cheese
可以写成 -ds -p cheese
甚至 -dsp cheese
。
选项的默认值
选项可以设置一个默认值。
program
.option('-c, --color', '给color选项设置一个蓝色的默认值', 'blue')
这样子直接运行 node index
获取的Options就是 { color: blue }
, 如果运行 node index -c
时还是得加上参数,不然会报错
选项取反
可以定义一个以no-
开头的 boolean 型长选项。在命令行中使用该选项时,会将对应选项的值置为false
。当只定义了带no-
的选项,未定义对应不带no-
的选项时,该选项的默认值会被置为true
。
const { program } = require('commander');
program
.option('--no-sauce', 'Remove sauce')
.option('--sauce', 'Remove sauce')
.option('--cheese <flavour>', 'cheese flavour', 'mozzarella')
.option('--no-cheese', 'plain with no cheese')
.parse();
const options = program.opts();
console.log(options);
运行
$ node index
// { sauce: true, cheese: 'mozzarella' }
$ node index --no-sauce
// { sauce: false, cheese: 'mozzarella' }
$ node index --sauce
// { sauce: true, cheese: 'mozzarella' }
$ node index --no-cheese
// { sauce: true, cheese: false }
可选参数
选项的参数使用方括号声明表示参数是可选参数(如--optional [value]
)。该选项在不带参数时可用作 boolean 选项,在带有参数时则从参数中得到值。
const { program } = require('commander');
program
.option('-c, --cheese [type]', '这是一个可选参数');
program.parse();
const options = program.opts();
console.log(options);
运行
$ node index -c
// { cheese: true }
$ node index -c xxx
// { cheese: 'xx' }
变长参数选项
定义选项时,可以通过使用...
来设置参数为可变长参数。在命令行中,用户可以输入多个参数,解析后会以数组形式存储在对应属性字段中。
const { program } = require('commander');
program
.option('-n, --number <numbers...>', 'specify numbers')
.option('-l, --letter [letters...]', 'specify letters');
program.parse();
const options = program.opts();
console.log('process.argv', process.argv);
console.log(options);
运行:
$ node index -n 1 2 3
// { number: [ '1', '2', '3' ] }
版本选项
.version()
方法可以设置版本,其默认选项为-V
和--version
,设置了版本后,命令行会输出当前的版本号。
program.version('0.0.1')
运行
$ node index -V
// 0.0.1
$ node index --version
其他选项配置
大多数情况下,选项均可通过.option()
方法添加。但对某些不常见的用例,也可以直接构造Option
对象,对选项进行更详尽的配置。
const { program, Option } = require('commander');
program
.addOption(new Option('-s, --secret').hideHelp())
.addOption(new Option('-t, --timeout <delay>', 'timeout in seconds').default(60, 'one minute'))
.addOption(new Option('-d, --drink <size>', 'drink size').choices(['small', 'medium', 'large']))
.addOption(new Option('-p, --port <number>', 'port number').env('PORT'))
.addOption(new Option('--donate [amount]', 'optional donation in dollars').preset('20').argParser(parseFloat))
.addOption(new Option('--disable-server', 'disables the server').conflicts('port'))
.addOption(new Option('--free-drink', 'small drink included free ').implies({ drink: 'small' }));
program.parse();
const options = program.opts();
console.log(options);
自定义选项处理
选项的参数可以通过自定义函数来处理,该函数接收两个参数,即用户新输入的参数值和当前已有的参数值(定义的默认值),返回新的选项参数值。
const { program } = require('commander');
function myParseInt(value, dummyPrevious) {
// parseInt 参数为字符串和进制数
// value: 传递的选项值
// dummyPrevious: 默认值: 20
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
throw new commander.InvalidArgumentError('Not a number.');
}
return parsedValue;
}
program
.option('-i, --integer <number>', 'integer argument', myParseInt, 20)
;
program.parse();
const options = program.opts();
console.log(options);
运行
$ node index -i
// { integer: 20 }
$ node index -i 16.89
// { integer: 16 }
命令
通过.command()
可以配置命令,有两种实现方式:为命令绑定处理函数,或者将命令单独写成一个可执行文件。
.command()
的第一个参数为命令名称。命令参数可以跟在名称后面,也可以用.argument()
单独指定。参数可为必选的(尖括号表示)、可选的(方括号表示)或变长参数(点号表示,如果使用,只能是最后一个参数)。
// 通过绑定处理函数实现命令(这里的指令描述为放在`.command`中)
// 返回新生成的命令(即该子命令)以供继续配置
program
.command('clone <source> [destination]')
.description('clone a repository into a newly created directory')
.action((source, destination) => {
console.log('clone command called');
});
// 通过独立的的可执行文件实现命令 (注意这里指令描述是作为`.command`的第二个参数)
// 返回最顶层的命令以供继续添加子命令
program
.command('start <service>', 'start named service')
.command('stop [service]', 'stop named service, or all if no name supplied');
运行
Options:
-h, --help display help for command
Commands:
clone <source> [destination] clone a repository into a newly created directory
start <service> start named service
stop [service] stop named service, or all if no name supplied
help [command] display help for command
命令参数
如上所述,子命令的参数可以通过.command()
指定。对于有独立可执行文件的子命令来说,参数只能以这种方法指定。而对其他子命令,参数也可用以下方法。
在Command
对象上使用.argument()
来按次序指定命令参数。该方法接受参数名称和参数描述。参数可为必选的(尖括号表示,例如<required>
)或可选的(方括号表示,例如[optional]
)
const { program } = require('commander');
program
.version('0.1.0')
.argument('<username>', 'user to login')
.argument('[password]', 'password for user, if required', 'no password given')
.action((username, password) => {
console.log('username:', username);
console.log('password:', password);
});
program.parse();
运行
$ node index blink 123456
// username: blink password: 123456
自定义参数处理
选项的参数可以通过自定义函数来处理(与处理选项参数时类似),该函数接收两个参数:用户新输入的参数值和当前已有的参数值(定义的默认值),返回新的命令参数值。
program
.command('add')
.argument('<first>', 'integer argument', myParseInt)
.argument('[second]', 'integer argument', myParseInt, 1000)
.action((first, second) => {
console.log(`${first} + ${second} = ${first + second}`);
})
;
处理函数
命令处理函数的参数,为该命令声明的所有参数,除此之外还会附加两个额外参数:一个是解析出的选项,另一个则是该命令对象自身。
const { program } = require('commander');
program
.argument('<name>')
.option('-t, --title <honorific>', 'title to use before name')
.option('-d, --debug', 'display some debugging')
.action((name, options, command) => {
console.log('name', name);
console.log('options', options);
console.log('command', command);
});
program.parse();
运行
node index add -t titlename
// name title
// options { title: titlename }
// command 该命令对象自身
这是一个使用子命令并带有帮助描述的更完整的程序。在多命令程序中,每个命令(或命令的独立可执行文件)都有一个操作处理程序。
const { Command } = require('commander');
const program = new Command();
program
.name('string-util')
.description('CLI to some JavaScript string utilities')
.version('0.8.0');
program.command('split')
.description('Split a string into substrings and display as an array')
.argument('<string>', 'string to split')
.option('--first', 'display just the first substring')
.option('-s, --separator <char>', 'separator character', ',')
.action((str, options) => {
const limit = options.first ? 1 : undefined;
console.log(str.split(options.separator, limit));
});
program.parse();