Node.js的命令行工具commander(汉化版)
npm install @yicode/commander> 由原代码仓库进行精简和汉化,感谢原作者!




完整的 node.js 命令行解决方案。
使用其他语言阅读:English | 简体中文
- Commander.js
- 安装
- 快速开始
- 声明 program 变量
- 选项
- 常用选项类型,boolean 型选项和带参数的选项
- 选项的默认值
- 其他的选项类型,取反选项,以及可选参数的选项
- 必填选项
- 变长参数选项
- 版本选项
- 其他选项配置
- 自定义选项处理
- 命令
- 命令参数
- 其他参数配置
- 自定义参数处理
- 处理函数
- 独立的可执行(子)命令
- 生命周期钩子
- 自动化帮助信息
- 自定义帮助
- 在出错后展示帮助信息
- 使用代码展示帮助信息
- .name
- .usage
- .description 和 .summary
- .helpOption(flags, description)
- .addHelpCommand()
- 其他帮助配置
- 自定义事件监听
- 零碎知识
- .parse() 和 .parseAsync()
- 解析配置
- 作为属性的遗留选项
- TypeScript
- createCommand()
- Node 选项,如 --harmony
- 调试子命令
- 显示错误
- 重写退出和输出
- 其他文档
- 支持
- 企业使用 Commander
关于本文档中使用的术语,请见术语表
``sh`
npm install commander
编写代码来描述你的命令行界面。
Commander 负责将参数解析为选项和命令参数,为问题显示使用错误,并实现一个有帮助的系统。
Commander 是严格的,并且会针对无法识别的选项显示错误。
两种最常用的选项类型是布尔选项,和从参数中获取值的选项。
示例代码:split.js
`js
const { program } = require('commander');
program.option('--first').option('-s, --separator
program.parse();
const options = program.opts();
const limit = options.first ? 1 : undefined;
console.log(program.args[0].split(options.separator, limit));
`
`console`
$ node split.js -s / --fits a/b/c
error: unknown option '--fits'
(Did you mean --first?)
$ node split.js -s / --first a/b/c
[ 'a' ]
这是一个使用子命令并带有帮助描述的更完整的程序。在多命令程序中,每个命令(或命令的独立可执行文件)都有一个操作处理程序。
示例代码:string-util.js
`js
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('
.option('--first', 'display just the first substring')
.option('-s, --separator
.action((str, options) => {
const limit = options.first ? 1 : undefined;
console.log(str.split(options.separator, limit));
});
program.parse();
`
`console
$ node string-util.js help split
Usage: string-util split [options]
Split a string into substrings and display as an array.
Arguments:
string string to split
Options:
--first display just the first substring
-s, --separator
-h, --help display help for command
$ node string-util.js split --separator=/ a/b/c
[ 'a', 'b', 'c' ]
`
更多示例可以在 examples 目录中找到。
为简化使用,Commander 提供了一个全局对象。本文档的示例代码均按此方法使用:
`js`
// CommonJS (.cjs)
const { program } = require('commander');
如果程序较为复杂,用户需要以多种方式来使用 Commander,如单元测试等。创建本地 Command 对象是一种更好的方式:
`js`
// CommonJS (.cjs)
const { Command } = require('commander');
const program = new Command();
`js`
// ECMAScript (.mjs)
import { Command } from 'commander';
const program = new Command();
`ts`
// TypeScript (.ts)
import { Command } from 'commander';
const program = new Command();
Commander 使用.option()方法来定义选项,同时可以附加选项的简介。每个选项可以定义一个短选项名称(-后面接单个字符)和一个长选项名称(--后面接一个或多个单词),使用逗号、空格或|分隔。
解析后的选项可以通过Command对象上的.opts()方法获取,同时会被传递给命令处理函数。
对于多个单词的长选项,选项名会转为驼峰命名法(camel-case),例如--template-engine选项可通过program.opts().templateEngine获取。
选项及其选项参数可以用空格分隔,也可以组合成同一个参数。选项参数可以直接跟在短选项之后,也可以在长选项后面加上 =。
`sh`
serve -p 80
serve -p80
serve --port 80
serve --port=80
--可以标记选项的结束,后续的参数均不会被命令解释,可以正常使用。
默认情况下,选项在命令行中的顺序不固定,一个选项可以在其他参数之前或之后指定。
当.opts()不够用时,还有其他相关方法:
- .optsWithGlobals()返回合并的本地和全局选项值.getOptionValue()
- 和.setOptionValue()操作单个选项的值.getOptionValueSource()
- 和.setOptionValueWithSource()包括选项值的来源
有两种最常用的选项,一类是 boolean 型选项,选项无需配置参数,另一类选项则可以设置参数(使用尖括号声明在该选项后,如--expect )。如果在命令行中不指定具体的选项及参数,则会被定义为undefined。
示例代码:options-common.js
`js
program.option('-d, --debug', 'output extra debugging').option('-s, --small', 'small pizza size').option('-p, --pizza-type
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});`
`console`
$ pizza-options -p
error: option '-p, --pizza-type
$ pizza-options -d -s -p vegetarian
{ debug: true, small: true, pizzaType: 'vegetarian' }
pizza details:
- small pizza size
- vegetarian
$ pizza-options --pizza-type=cheese
pizza details:
- cheese
多个布尔短选项可以在破折号之后组合在一起,并且可以跟一个取值的单一选项。
例如 -d -s -p cheese 可以写成 -ds -p cheese 甚至 -dsp cheese。
具有预期选项参数的选项是贪婪的,并且无论值如何,都会消耗参数。
所以 --id -xyz 读取 -xyz 作为选项参数。
通过program.parse(arguments)方法处理参数,没有被使用的选项会存放在program.args数组中。该方法的参数是可选的,默认值为process.argv。
选项可以设置一个默认值。
示例代码:options-defaults.js
`js
program.option('-c, --cheese
program.parse();
console.log(cheese: ${program.opts().cheese});`
`console`
$ pizza-options
cheese: blue
$ pizza-options --cheese stilton
cheese: stilton
可以定义一个以no-开头的 boolean 型长选项。在命令行中使用该选项时,会将对应选项的值置为false。当只定义了带no-的选项,未定义对应不带no-的选项时,该选项的默认值会被置为true。
如果已经定义了--foo,那么再定义--no-foo并不会改变它本来的默认值。
示例代码:options-negatable.js
`js
program.option('--no-sauce', 'Remove sauce').option('--cheese
const options = program.opts();
const sauceStr = options.sauce ? 'sauce' : 'no sauce';
const cheeseStr = options.cheese === false ? 'no cheese' : ${options.cheese} cheese;You ordered a pizza with ${sauceStr} and ${cheeseStr}
console.log();`
`console`
$ pizza-options
You ordered a pizza with sauce and mozzarella cheese
$ pizza-options --sauce
error: unknown option '--sauce'
$ pizza-options --cheese=blue
You ordered a pizza with sauce and blue cheese
$ pizza-options --no-sauce --no-cheese
You ordered a pizza with no sauce and no cheese
选项的参数使用方括号声明表示参数是可选参数(如--optional [value])。该选项在不带参数时可用作 boolean 选项,在带有参数时则从参数中得到值。
示例代码:options-boolean-or-value.js
`js
program.option('-c, --cheese [type]', 'Add cheese with optional type');
program.parse(process.argv);
const options = program.opts();
if (options.cheese === undefined) console.log('no cheese');
else if (options.cheese === true) console.log('add cheese');
else console.log(add cheese type ${options.cheese});`
`console`
$ pizza-options
no cheese
$ pizza-options --cheese
add cheese
$ pizza-options --cheese mozzarella
add cheese type mozzarella
带有可选选项参数的选项不是贪婪的,并且会忽略以破折号开头的参数。因此对于--id -5,id表现为布尔选项,但如果需要,您可以使用组合形式,例如 --id=-5。
关于可能有歧义的用例,请见可变参数的选项。
通过.requiredOption()方法可以设置选项为必填。必填选项要么设有默认值,要么必须在命令行中输入,对应的属性字段在解析时必定会有赋值。该方法其余参数与.option()一致。
示例代码:options-required.js
`js
program.requiredOption('-c, --cheese
program.parse();
`
`console`
$ pizza
error: required option '-c, --cheese
定义选项时,可以通过使用...来设置参数为可变长参数。在命令行中,用户可以输入多个参数,解析后会以数组形式存储在对应属性字段中。在输入下一个选项前(-或--开头),用户输入的指令均会被视作变长参数。与普通参数一样的是,可以通过--标记当前命令的结束。
示例代码:options-variadic.js
`js
program.option('-n, --number
program.parse();
console.log('Options: ', program.opts());
console.log('Remaining arguments: ', program.args);
`
`console`
$ collect -n 1 2 3 --letter a b c
Options: { number: [ '1', '2', '3' ], letter: [ 'a', 'b', 'c' ] }
Remaining arguments: []
$ collect --letter=A -n80 operand
Options: { number: [ '80' ], letter: [ 'A' ] }
Remaining arguments: [ 'operand' ]
$ collect --letter -n 1 -n 2 3 -- operand
Options: { number: [ '1', '2', '3' ], letter: true }
Remaining arguments: [ 'operand' ]
关于可能有歧义的用例,请见可变参数的选项。
.version()方法可以设置版本,其默认选项为-V和--version,设置了版本后,命令行会输出当前的版本号。
`js`
program.version('0.0.1');
`console`
$ ./examples/pizza -V
0.0.1
版本选项也支持自定义设置选项名称,可以在.version()方法里再传递一些参数(长选项名称、描述信息),用法与.option()方法类似。
`bash`
program.version('0.0.1', '-v, --vers', 'output the current version');
大多数情况下,选项均可通过.option()方法添加。但对某些不常见的用例,也可以直接构造Option对象,对选项进行更详尽的配置。
示例代码:options-extra.js, options-env.js, options-conflicts.js, options-implies.js
`js`
program
.addOption(new Option('-s, --secret').hideHelp())
.addOption(new Option('-t, --timeout
.addOption(new Option('-d, --drink
.addOption(new Option('-p, --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' }));
`console
$ extra --help
Usage: help [options]
Options:
-t, --timeout
-d, --drink
-p, --port
--donate [amount] optional donation in dollars (preset: "20")
--disable-server disables the server
--free-drink small drink included free
-h, --help display help for command
$ extra --drink huge
error: option '-d, --drink
$ PORT=80 extra --donate --free-drink
Options: { timeout: 60, donate: 20, port: '80', freeDrink: true, drink: 'small' }
$ extra --disable-server --port 8000
error: option '--disable-server' cannot be used with option '-p, --port
`
选项的参数可以通过自定义函数来处理,该函数接收两个参数,即用户新输入的参数值和当前已有的参数值(即上一次调用自定义处理函数后的返回值),返回新的选项参数值。
自定义函数适用场景包括参数类型转换,参数暂存,或者其他自定义处理的场景。
可以在自定义函数的后面设置选项参数的默认值或初始值(例如参数用列表暂存时需要设置一个初始空列表)。
示例代码:options-custom-processing.js
`js
function myParseInt(value, dummyPrevious) {
// parseInt 参数为字符串和进制数
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
throw new commander.InvalidArgumentError('Not a number.');
}
return parsedValue;
}
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
program.parse();
const options = program.opts();
if (options.float !== undefined) console.log(float: ${options.float});integer: ${options.integer}
if (options.integer !== undefined) console.log();verbosity: ${options.verbose}
if (options.verbose > 0) console.log();`
if (options.collect.length > 0) console.log(options.collect);
if (options.list !== undefined) console.log(options.list);
`console`
$ 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' ]
通过.command()或.addCommand()可以配置命令,有两种实现方式:为命令绑定处理函数,或者将命令单独写成一个可执行文件(详述见后文)。子命令支持嵌套(示例代码)。
.command()的第一个参数为命令名称。命令参数可以跟在名称后面,也可以用.argument()单独指定。参数可为必选的(尖括号表示)、可选的(方括号表示)或变长参数(点号表示,如果使用,只能是最后一个参数)。
使用.addCommand()向program增加配置好的子命令。
例如:
`js.command
// 通过绑定处理函数实现命令(这里的指令描述为放在中)
// 返回新生成的命令(即该子命令)以供继续配置
program
.command('clone
.description('clone a repository into a newly created directory')
.action((source, destination) => {
console.log('clone command called');
});
// 通过独立的的可执行文件实现命令 (注意这里指令描述是作为.command的第二个参数)
// 返回最顶层的命令以供继续添加子命令
program.command('start
// 分别装配命令
// 返回最顶层的命令以供继续添加子命令
program.addCommand(build.makeBuildCommand());
`
使用.command()和addCommand()来指定选项的相关设置。当设置hidden: true时,该命令不会打印在帮助信息里。当设置isDefault: true时,若没有指定其他子命令,则会默认执行这个命令(样例)。
如上所述,子命令的参数可以通过.command()指定。对于有独立可执行文件的子命令来说,参数只能以这种方法指定。而对其他子命令,参数也可用以下方法。
在Command对象上使用.argument()来按次序指定命令参数。该方法接受参数名称和参数描述。参数可为必选的(尖括号表示,例如)或可选的(方括号表示,例如[optional])。
示例代码:argument.js
`js`
program
.version('0.1.0')
.argument('
.argument('[password]', 'password for user, if required', 'no password given')
.action((username, password) => {
console.log('username:', username);
console.log('password:', password);
});
在参数名后加上...来声明可变参数,且只有最后一个参数支持这种用法。可变参数会以数组的形式传递给处理函数。例如:
`js`
program
.version('0.1.0')
.command('rmdir')
.argument('
.action(function (dirs) {
dirs.forEach((dir) => {
console.log('rmdir %s', dir);
});
});
有一种便捷方式可以一次性指定多个参数,但不包含参数描述:
`js`
program.arguments('
#### 其他参数配置
有少数附加功能可以直接构造Argument对象,对参数进行更详尽的配置。
示例代码:arguments-extra.js
`js`
program.addArgument(new commander.Argument('
#### 自定义参数处理
选项的参数可以通过自定义函数来处理(与处理选项参数时类似),该函数接收两个参数:用户新输入的参数值和当前已有的参数值(即上一次调用自定义处理函数后的返回值),返回新的命令参数值。
处理后的参数值会传递给命令处理函数,同时可通过.processedArgs获取。
可以在自定义函数的后面设置命令参数的默认值或初始值。
示例代码:arguments-custom-processing.js
`js${first} + ${second} = ${first + second}
program
.command('add')
.argument('
.argument('[second]', 'integer argument', myParseInt, 1000)
.action((first, second) => {
console.log();`
});
命令处理函数的参数,为该命令声明的所有参数,除此之外还会附加两个额外参数:一个是解析出的选项,另一个则是该命令对象自身。
示例代码:thank.js
`js${options.title}
program
.argument('
.option('-t, --title
.option('-d, --debug', 'display some debugging')
.action((name, options, command) => {
if (options.debug) {
console.error('Called %s with options %o', command.name(), options);
}
const title = options.title ? : '';Thank-you ${title}${name}
console.log();`
});
如果你愿意,你可以跳过为处理函数声明参数直接使用 command。 this 关键字设置为运行命令,可以在函数表达式中使用(但不能从箭头函数中使用)。
示例代码:action-this.js
`js
program
.command('serve')
.argument('