简介

事实上,在Nodejs或ruby等语言环境里,只要在文件头部添加一行所谓的shebang(提供一个执行环境),就可以将代码转为命令行执行。比如在Node中文件头这样写:

#!/usr/bin/env node

但是命令行选项处理和流程控制很不方便,所以才有了命令行框架的出现。Commander 是一款重量轻,表现力和强大的命令行框架。提供了用户命令行输入和参数解析强大功能。Commander 源自一个同名的Ruby项目。它 是 TJ 写的一个帮助快速开发Nodejs命令行工具的package。

安装

在项目根目录执行:

npm install commander --save

选项

指令带的选项由.option()定义,也会作为选项的文档。下面的例子从process.argv解析了选项和参数,没有被选项占用的参数会作为program.args数组。所有的属性带[]就是可选的,带<>的是必需参数。

option()接收四个参数

  • 第一个参数是属性,可以使用逗号分隔开
  • 第二个为 option 描述, 会在 help 信息里展示出来
  • 第三个参数为回调函数
  • 第四个参数为默认值
#!/usr/bin/env node
/**
* Module dependencies.
*/
var program = require('commander');
program
  .version('0.1.0')
  .option('-p, --peppers', 'Add peppers')
  .option('-P, --pineapple', 'Add pineapple')
  .option('-b, --bbq-sauce', 'Add bbq sauce')
  .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')
  .parse(process.argv);
console.log('you ordered a pizza with:');
if (program.peppers) console.log('  - peppers');
if (program.pineapple) console.log('  - pineapple');
if (program.bbqSauce) console.log('  - bbq');
console.log('  - %s cheese', program.cheese);

短属性会被解析成单独的一个参数。例如-abc会被解析成-a -b -c,多单词的选项比如--template-engine会被解析成驼峰式templateEngine

如果多单词的选项是以--no为前缀的,则代表是否定的,如:--no-sauce则代表program.sauce的值为false

在option的第一个参数里, 除了shortlong option,还可以指定option类型,分隔符也是|, 其中

  • <...flag> required参数,使用时后边必须跟参数值, 否则程序会报错
  • [flag] optional参数,后面可以选择是否跟参数值
  • 如果以--no则表示默认为false(没有使用--no默认值为true
function range(val) {
  return val.split('..').map(Number);
}
function list(val) {
  return val.split(',');
}
function collect(val, memo) {
  memo.push(val);
  return memo;
}
function increaseVerbosity(v, total) {
  return total + 1;
}
program
  .version('0.1.0')
  .usage('[options] <file ...>')
  .option('-i, --integer <n>', 'An integer argument', parseInt)
  .option('-f, --float <n>', 'A float argument', parseFloat)
  .option('-r, --range <a>..<b>', 'A range', range)
  .option('-l, --list <items>', 'A list', list)
  .option('-o, --optional [value]', 'An optional value')
  .option('-c, --collect [value]', 'A repeatable value', collect, [])
  .option('-v, --verbose', 'A value that can be increased', increaseVerbosity, 0)
  .option('-d, --no-date', 'don‘t display current date')
  .parse(process.argv);
console.log(' int: %j', program.integer);
console.log(' float: %j', program.float);
console.log(' optional: %j', program.optional);
program.range = program.range || [];
console.log(' range: %j..%j', program.range[0], program.range[1]);
console.log(' list: %j', program.list);
console.log(' collect: %j', program.collect);
console.log(' verbosity: %j', program.verbose);
console.log(' args: %j', program.args);

option的第三个参数不仅可以是回调函数,也可以为一个正则表达式:

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

如果匹配成功则显示匹配值,匹配不成功则显示默认值或者true或者false,没有这个选项的话则为undefined

版本号

向你的项目中添加版本号,当在命令行中使用-V或者--version的时候显示版本号。

$ ./examples/pizza -V
0.0.1

如果你想使用-v而不是-V来显示版本号,只要自定义版本号的选项即可,版本的属性选项可以是任何名字,但是长属性不能省略。

program
  .version('0.0.1', '-v, --version')

Command-具体的选项

你也可以为一个命令添加若干选项:

#!/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)

一个命令的选项只有在这个命令使用的时候才可以使用,任何未知的选项都会报错,如果一个基于action的命令没有定义action,那么选项也是不合法的。

可变的参数

command的最后一个参数可以是一个可变的参数,如果想要使用可变的参数的话你要在参数名字后面加上...:

#!/usr/bin/env node
/**
 * Module dependencies.
 */
var program = require('commander');
program
  .version('0.1.0')
  .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);

一个数组会用在可变的参数里面,这不仅会应用在program.args也会传递给你的action中显示。

具体化参数语法

我们可以使用arguments()来更加具体地处理参数,注意这个参数是除了选项属性之外的参数。

#!/usr/bin/env node
var program = require('commander');
program
  .version('0.1.0')
  .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");
node env -a b cd #command: c environment: d
node env a b # command: a environment: b

类Git风格子命令

command()被调用的时候且有description这个参数,不能有action()调用处理子命令,否则的话会报错。这会告诉commander你要使用分离的可执行文件对于子命令,就像Git和一些其他的工具一样。

commander会尝试去在目录里的入口脚本搜索同名的可执行的文件,例如pm-installpm-search

选项会被传递进command(),填写opts.noHelp:true会移除生成help输出,填写opts.isDefaut会运行这个子命令如果没有子命令被指定运行的话。

如果你的程序想要被设计成全局安装,注意可执行文件拥有正确的权限,比如755

// file: ./examples/pm
var program = require('commander');

program
  .version('0.1.0')
  .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);

事件监听

命名多少个命令就监听多少命令,--help 为默认监听事件。

program.on('--help', function(argv,test){
    process.exit(1);
});

以和谐方式执行--harmony

你可以有两种方式使用--harmony参数:

  • #! /usr/bin/env node --harmony在子命令脚本头加入,但是可能有些操作系统不支持
  • 使用--harmony在你使用命令行的时候

自动生成帮助信息--help

Commander会根据配置的optionsub-command等信息,自动生成help信息。

自定义帮助信息

可以通过监听--help事件来输出额外的帮助信息

#!/usr/bin/env node
/**
 * Module dependencies.
 */
var program = require('commander');
program
  .version('0.1.0')
  .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('  Examples:');
  console.log('');
  console.log('    $ custom-help --help');
  console.log('    $ custom-help -h');
  console.log('');
});
program.parse(process.argv);
console.log('stuff');

.outputHelp(cb)

输出帮助信息并且不退出,可选的回调函数允许处理帮助信息在展示之前。

.help(cb)

输出帮助信息立刻退出。

参考资料