Skip to content

Latest commit

 

History

History
193 lines (147 loc) · 6.21 KB

可变参数的选项.md

File metadata and controls

193 lines (147 loc) · 6.21 KB

可变参数的选项

README 文档介绍了选项的声明与使用。大多数情况下,选项的解析行为,与您和您程序用户的期望相符。本文将详述一些特殊用例和细节问题。

有些选项可以接受可变数量的参数:

program
   .option('-c, --compress [percentage]') // 0 或 1 个参数
   .option('--preprocess <file...>') // 至少 1 个参数
   .option('--test [name...]') // 0 或多个参数

本文中使用的示例代码接受 0 或 1 个参数。但相关的讨论对接受更多参数的选项同样适用。

关于本文档中使用的术语,参见术语表

解析中的歧义

解析选项时,有一些潜在的问题值得注意。当一个命令既有命令参数,又有选项的时候,在解析过程中有可能出现歧义。这可能会影响用户使用您的程序。 Commander 首先解析选项的参数,而用户有可能想将选项后面跟的参数作为命令,或是作为命令参数进行解析。

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();
$ 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

可以通过使用--来表示选项和选项参数部分的结束,以此来显式地解决这一冲突:

$ node cook.js -i -- scrambled
technique: scrambled
ingredient: cheese

如果不希望强制用户掌握这种使用--的写法,您可以尝试以下几种方案。

方案一:让--成为语法的一部分

与其让用户理解--的用法,您也可以直接把它作为程序语法的一部分。

program.usage('[options] -- [technique]');
$ 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 解析命令的惯例,允许选项出现在命令参数之前或者之后,也可以将选项和命令参数相互穿插。 因此,通过要求把选项放在最后,命令参数就不会和选项参数相混淆。

program.usage('[technique] [options]');
$ 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

方案三:使用选项替代命令参数

这个方案虽然有点激进,但是可以完美避免解析中的歧义。

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}`);
  });
$ cook -i -t scrambled
technique: scrambled
ingredient: cheese

合并短选项和带有参数的选项

多个 boolean 型的短选项可以合并起来写在同一个-号后面,比如ls -al。同时,你也可以在其中包括一个接受参数的短选项,此时,任何跟在其后的字符都将被视作这个选项的值。

这就是说,默认情况下并不可以把多个带参数的选项合并起来。

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}`);
$ 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 类型,可以改变此行为:

.combineFlagAndOptionalValue(true)  // `-v45` 被视为 `--vegan=45`,这是默认的行为
.combineFlagAndOptionalValue(false) // `-vl` 被视为 `-v -l`