Appearance
可变参数的选项
README 文档介绍了选项的声明与使用。大多数情况下,选项的解析行为,与您和您程序用户的期望相符。本文将详述一些特殊用例和细节问题。
有些选项可以接受可变数量的参数:
js
program
.option("-c, --compress [percentage]") // 0 或 1 个参数
.option("--preprocess <file...>") // 至少 1 个参数
.option("--test [name...]"); // 0 或多个参数
本文中使用的示例代码接受 0 或 1 个参数。但相关的讨论对接受更多参数的选项同样适用。
关于本文档中使用的术语,参见术语表。
解析中的歧义
解析选项时,有一些潜在的问题值得注意。当一个命令既有命令参数,又有选项的时候,在解析过程中有可能出现歧义。这可能会影响用户使用您的程序。 Commander 首先解析选项的参数,而用户有可能想将选项后面跟的参数作为命令,或是作为命令参数进行解析。
js
program
.name("cook")
.argument("[technique]")
.option("-i, --ingredient [ingredient]", "add cheese or given ingredient")
.action((technique, options) => {
console.log(`technique: ${technique}`);
const ingredient =
options.ingredient === true ? "cheese" : options.ingredient;
console.log(`ingredient: ${ingredient}`);
});
program.parse();
sh
$ cook scrambled
technique: scrambled
ingredient: undefined
$ cook -i
technique: undefined
ingredient: cheese
$ cook -i egg
technique: undefined
ingredient: egg
$ cook -i scrambled # oops
technique: undefined
ingredient: scrambled
可以通过使用--
来表示选项和选项参数部分的结束,以此来显式地解决这一冲突:
sh
$ node cook.js -i -- scrambled
technique: scrambled
ingredient: cheese
如果不希望强制用户掌握这种使用--
的写法,您可以尝试以下几种方案。
方案一:让--
成为语法的一部分
与其让用户理解--
的用法,您也可以直接把它作为程序语法的一部分。
js
program.usage("[options] -- [technique]");
sh
$ cook --help
Usage: cook [options] -- [technique]
Options:
-i, --ingredient [ingredient] add cheese or given ingredient
-h, --help display help for command
$ cook -- scrambled
technique: scrambled
ingredient: undefined
$ cook -i -- scrambled
technique: scrambled
ingredient: cheese
方案二:把选项放在最后
Commander 遵循 GNU 解析命令的惯例,允许选项出现在命令参数之前或者之后,也可以将选项和命令参数相互穿插。 因此,通过要求把选项放在最后,命令参数就不会和选项参数相混淆。
js
program.usage("[technique] [options]");
sh
$ cook --help
Usage: cook [technique] [options]
Options:
-i, --ingredient [ingredient] add cheese or given ingredient
-h, --help display help for command
$ node cook.js scrambled -i
technique: scrambled
ingredient: cheese
方案三:使用选项替代命令参数
这个方案虽然有点激进,但是可以完美避免解析中的歧义。
js
program
.name("cook")
.option("-t, --technique <technique>", "cooking technique")
.option("-i, --ingredient [ingredient]", "add cheese or given ingredient")
.action((options) => {
console.log(`technique: ${options.technique}`);
const ingredient =
options.ingredient === true ? "cheese" : options.ingredient;
console.log(`ingredient: ${ingredient}`);
});
sh
$ cook -i -t scrambled
technique: scrambled
ingredient: cheese
合并短选项和带有参数的选项
多个 boolean 型的短选项可以合并起来写在同一个-
号后面,比如ls -al
。同时,你也可以在其中包括一个接受参数的短选项,此时,任何跟在其后的字符都将被视作这个选项的值。
这就是说,默认情况下并不可以把多个带参数的选项合并起来。
js
program
.name("collect")
.option("-o, --other [count]", "other serving(s)")
.option("-v, --vegan [count]", "vegan serving(s)")
.option("-l, --halal [count]", "halal serving(s)");
program.parse(process.argv);
const opts = program.opts();
if (opts.other) console.log(`other servings: ${opts.other}`);
if (opts.vegan) console.log(`vegan servings: ${opts.vegan}`);
if (opts.halal) console.log(`halal servings: ${opts.halal}`);
sh
$ collect -o 3
other servings: 3
$ collect -o3
other servings: 3
$ collect -l -v
vegan servings: true
halal servings: true
$ collect -lv # oops
halal servings: v
如果需要使用多个既可用作 boolean,又可以接受参数的选项,只能把它们分别声明。
$ collect -a -v -l
any servings: true
vegan servings: true
halal servings: true
将短选项视作 boolean 类型选项合并
在 Commander v5 之前,并不支持合并短选项和值。合并的 boolean 类型选项会被展开。 因此,-avl
将被展开为-a -v -l
。
如果您需要后向兼容,或是倾向于在短选项合并的时候把它们视作 boolean 类型,可以改变此行为:
js
.combineFlagAndOptionalValue(true) // `-v45` 被视为 `--vegan=45`,这是默认的行为
.combineFlagAndOptionalValue(false) // `-vl` 被视为 `-v -l`