看啥推荐读物
专栏名称: litongqian
前端开发
目录
相关文章推荐
今天看啥  ›  专栏  ›  litongqian

commander编写Node命令行工具(cli)

litongqian  · 掘金  ·  · 2019-04-26 05:57
阅读 20

commander编写Node命令行工具(cli)

随处可见的cli工具,

例如vue-cli:

其中解析参数使用的都是commander模块,要看懂工具干了啥,理解commander就格外重要了!

原理:

当一个Nodejs程序运行时,process对象中有一个叫做argv的属性。命令行程序的第一个重头戏就是解析这个process.argv属性。

定义一个hello脚本,打印一下process.argv

#!/usr/bin/env node
console.log('hello ', process.argv);复制代码
在终端输入$ hello a b c

看起来process.argv好像是一个数组,其中第一个元素是node的执行路径,第二个元素是当前执行文件的路径,从第三个元素开始,是执行时带入的参数。

当然可以自己解析argv参数

使用commander.js

commander.jsTJ所写的一个工具包,其作用是让node命令行程序的制作更加简单。

安装及使用

安装很简单,

$ npm install commander复制代码

// file: ./bisheng
#!/usr/bin/env nodeconst program = require('commander');const package = require('../package.json');program  .version(package.version)  .option('-f, --foo', 'enable some foo')  .option('-b, --bar', 'enable some bar')  .option('-B, --baz', 'enable some baz');program.on('--help', function(){  console.log('');  console.log('Examples:');  console.log('  $ custom-help --help');  console.log('  $ custom-help -h');});program.parse(process.argv);复制代码

命令来执行它$ ./bisheng -h复制代码


commander.js第一个优势就是提供了简介的api对可选项、参数进行解析。第二个优势就是自动生成帮助的文本信息。

常用api

  • version

作用:定义命令程序的版本号 用法示例:.version('0.0.1', '-v, --version') 参数解析:
  1. 版本号<必须>
  2. 自定义标志<可省略>:默认为 -V 和 --version


  • option

作用:用于定义命令选项:.option('-n, --name <name>', 'your name', 'GK')

  1. 第一个参数自定义标志<必须>:分为长短标识,中间用逗号、竖线或者空格分割;。
    1. 标志后面可跟参数,可以用<> 或者 []修饰,前者意为必须参数,后者意为可选参数
  2. 选项描述<省略不报错>:在使用 --help 命令时显示标志描述
  3. 第三个参数为选项参数默认值,可选。


  • command

作用:添加命令名称 用法示例:.command('rmdir [otherDirs...]', 'install description', opts)

参数解析:

  1. 命令名称<必须>:命令后面可跟用 <> 或 [] 包含的参数;命令的最后一个参数可以是可变的,像实例中那样在数组后面加入 otherDirs... 标志;在命令后面传入的参数会被传入到 action 的回调函数以及 program.args 数组中
  2. 命令描述<可省略>:如果存在,且没有显示调用action(fn),就会启动子命令程序,否则会报错
      • 当没有第二个参数时,commander.js将返回Command对象,若有第二个参数,将返回原型对象。
      • 当带有第二个参数,并且没有显示调用action(fn)时,则将会使用子命令模式。
      • 所谓子命令模式即,./pm./pm-install./pm-search等。这些子命令跟主命令在不同的文件中。
 3. 配置选项<可省略>:可配置noHelp、isDefault等


  • alias

         作用:自定义别名 用法 .alias('ex')


  • description

作用:定义命令的描述 用法示例:.description('rmdir desc')


  • action

用法:.action(fn)

用于设置命令执行的相关回调。fn可以接受命令的参数为函数形参,顺序与command()中定义的顺序一致。


  • parse

用法:program.parse(process.argv)

此api一般是最后调用,用于解析process.argv


  • outputHelp

用法:program.outputHelp()

一般用于未录入参数时自动打印帮助信息


参数解析

.option() 方法用来定义带选项的 commander,同时也作为这些选项的文档。下面的例子会解析来自 process.argv 指定的参数和选项,没有匹配任何选项的参数将会放到 program.args 数组中。

#!/usr/bin/env node

var program = require('commander');

program
  .version('0.0.1')
  .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);复制代码

执行命令$ ./bisheng -p 或者 ./bisheng --peppers

program.peppers的值为true复制代码


执行命令$ ./bisheng -c 123

program.cheese值为123 默认值为marble复制代码



添加处理函数

#!/usr/bin/env node
var program = require('commander');


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.0.1')
  .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)
  .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);复制代码
查看输出的对应结果如下:


正则表达式

program
  .version('0.0.1')
  .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);复制代码

可变参数

一个命令的最后一个参数可以是可变参数, 并且只有最后一个参数可变。为了使参数可变,你需要在参数名后面追加 ...。 下面是个示例:

#!/usr/bin/env node

var program = require('commander');

program
  .version('0.0.1')
  .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);复制代码
执行$ ./bisheng rmdir dir 五一 放假 回家
rmdir <dir> [otherDirs...]
dir 接受参数dir otherDirs为一个数组接受后面所有的参数(可变参数)复制代码


指定参数的语法

尖括号(例如 <cmd>)代表必填输入,方括号(例如 [env])代表可选输入。

#!/usr/bin/env node

var program = require('commander');

program
  .version('0.0.1')
  .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");复制代码


Git 风格的子命令

// file: ./bin/bisheng
#!/usr/bin/env nodeconst program = require('commander');const package = require('../package.json');program  .version(package.version)  .usage('[command] [options]')  .command('start [options]', 'to start a server')  .command('build [options]', 'to build and write static files to `config.output`')  .command('gh-pages [options]', 'to deploy website to gh-pages',{isDefault: true})  .parse(process.argv);复制代码

.command() 带有描述参数时,不能采用 .action(callback) 来处理子命令,否则会出错。这告诉 commander,你将采用单独的可执行文件作为子命令。 Commander 将会尝试在入口脚本(例如 ./bin/bisheng)的目录中搜索 program-command 形式的可执行文件,例如 bisheng-start, bisheng-build,bisheng-gh-pages

如下:


你可以在调用 .command() 时传递选项。指定 opts.noHelptrue 将从生成的帮助输出中剔除该选项。指定 opts.isDefaulttrue 将会在没有其它子命令指定的情况下,执行该子命令。

自动化帮助信息 --help

帮助信息是 commander 基于你的程序自动生成的,下面是 --help 生成的帮助信息:


自定义帮助

你可以通过监听 --help 来控制 -h, --help 显示任何信息。一旦调用完成, Commander 将自动退出,你的程序的其余部分不会展示。例如在下面的 “stuff” 将不会在执行 --help 时输出。

#!/usr/bin/env node
var program = require('commander');

program
  .version('0.0.1')
  .option('-f, --foo', 'enable some foo')
  .option('-b, --bar', 'enable some bar')
  .option('-B, --baz', 'enable some baz');

program.on('--help', function(){
  console.log('');
  console.log('Examples:');
  console.log('  $ custom-help --help');
  console.log('  $ custom-help -h');
});

program.parse(process.argv);

console.log('stuff');
复制代码


.outputHelp(cb)

不退出输出帮助信息。 可选的回调可在显示帮助文本后处理。 如果你想显示默认的帮助(例如,如果没有提供命令),你可以使用类似的东西:

var program = require('commander');
var colors = require('colors');

program
  .version('0.0.1')
  .command('getstream [url]', 'get stream URL')
  .parse(process.argv);

  if (!process.argv.slice(2).length) {
    program.outputHelp(make_red);
  }

function make_red(txt) {
  return colors.red(txt); // 在控制台上显示红色的帮助文本
}复制代码


  1. node.js 命令行工具(cli)
  2.  commander
  3. 使用commander.js做一个Nodejs命令行程序





原文地址:访问原文地址
快照地址: 访问文章快照