commanderJs编写命令行工具(cli)

前言:

  最近须要作一个内部的node cli来独立构建流程,对整个命令行工具实现流程有了大体了解,下面来解释一下如何实现一个cli,和如何使用 commander 库。node


新手误区:

  在开始实现以前,我知道有 commander 这个node库,有不少cli使用了它,对它的大体了解就是它能够帮助开发者简化实现命令流程。 因而最初觉得不利用库要去实现命令行会很麻烦,commander 帮咱们解决了各类兼容问题等等。 后来发现并非的,没有commander 咱们也能够不用化多大力气实现命令行,commander 仅仅是一个自己也不太复杂的封装(源码也只有1200行)。 咱们的第一步仍是应该先搞清楚,经过npm如何实现命令行。git


一个简单的cli:

bin:

  package.json 中有一个 bin 字段,指定各个内部命令对应的可执行文件的位置。 在包安装时,若是是全局安装,npm 将会把 package.json 里定义的 bin 文件软链接到全局 node_modules/bin,若是是非全局安装,会软连接到项目文件夹./node_modules/.bin/。 根据下面代码的配置,当咱们全局安装此包后,在任意位置运行 cli-test,都会执行全局 node_modules 中的 cli-test 文件。github

/*cli-test 的 package.json*/
"bin": { "cli-test": "./bin/cli-test" }
复制代码

若是咱们把cli安装在项目A node_modules中,经过设置项目中 package.json 的 scripts,运行 npm run cli,npm 就会在项目的 node_modules/.bin 寻找并运行 cli-test 文件。npm

/*项目A package.json*/
"scripts": { "cli": "cli-test" }
复制代码


cli文件:

  下面是 cli-test 文件,第一行必写,是告诉Unix和Linux系统这个文件中的代码用node可执行程序去运行它。 后面就作咱们要作的事情就好了 。json

#!/usr/bin/env node

//do something
复制代码

好了,到这里咱们的cli就完成了。 其实有不少三方cli也并无用相似 commander 的 node 库,若是咱们的 cli 足够简单,以上这样就能够了。 下面接着讲 commander。数组


commander:

如今咱们先写一个简单的文件来理解(也推荐先自行预览一下 commander 官方文档),下面是 bin 文件夹的 cli-test,代码以下:bash

#!/usr/bin/env node
 const program =require('commander');
program
.usage('\[option\]', '--type required')
.option('--type \[typeName\]', 'type: dev && build')
.parse(process.argv);

const {type} = program; if(type == 'dev'){
    console.log('do something', type)
}else if(type == 'build'){
    console.log('do something', type)
}else{
    console.log('params error');
    program.help();
}

复制代码


解释一下上面的代码,从查看源码里发现 require('commander') 会 new一个commander 内部的单例对象并返回,program 已是一个实例,。 .usage 仅仅描述了参数规则,会在 --help 中打印出来。.option 定义了一个参数名和描述, parse 会解析命令之中的参数,根据上面定义好的规则执行相关命令。 好比上面的代码定义了 option 类型的参数 --type,执行 .parse 的时候,parse 根据 process.argv 之中的参数,获取到 --type,并把参数命和参数值存储在内部 commander 实例的属性之中,所以后面的代码就能从 program 之中取到 type,若是 type 不存在或者不是咱们约定的值,最后咱们打印参数错误,并执行help方法打印了 --help。 以下截图,咱们 node 执行 cli-test,由于没有约定参数,因此执行了 else 的程序。(由于这里是本地的demo程序,因此直接使用node命令)工具


接着,咱们执行正确的命令参数,以下学习


这样一个简单的demo就实现了,看起来也挺简单的,commander 封装了一些也不算很复杂的功能。 ui


再来一个例子:

新建了两个文件,要以 bin 命令的执行文件命后面加上 -name,做为子命令文件


cli-test:

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

program .usage('<command> \[option\]', 'option --type required')
.command('h5', 'to h5')
.command('rn', 'to rn')
.parse(process.argv);
复制代码


cli-test-h5:

#!/usr/bin/env node
 const program =require('commander');
program
.option('--type \[typeName\]', 'type: dev && build')
.parse(process.argv);

const {type} = program; if(type == 'dev'){
    console.log('do something h5', type)
}else if(type == 'build'){
    console.log('do something h5', type)
}else{
    console.log('params error');
    program.help();
}

复制代码


cli-test-rn:

#!/usr/bin/env node
 const program =require('commander');
program
.option('--type \[typeName\]', 'type: dev && build')
.parse(process.argv);

const {type} = program; if(type == 'dev'){
    console.log('do something rn', type)
}else if(type == 'build'){
    console.log('do something rn', type)
}else{
    console.log('params error');
    program.help();
}

复制代码


先直接运行3个命令运行程序,看下结果,然后分别解释一下:



node ./bin/cli-test:

定义了.command子命令却没有相应执行参数,commander对象会直接打印-help,并process.exit退出进程。


node ./bin/cli-test h5 --type dev:

cli-test 经过 command 方法约定子命令名称和描述,如 h5,当执行 node cli-test h5 --type dev的时候,cli-test 执行到 .command('h5', 'to h5') ,会在当前 commander 实例内部,new 一个 name 为 h5 的子 commander,存储在当前父实例的 commands 数组中,当 .parse(process.argv) 执行,获取到参数中 h5 后,在 commands 里查找是否有 name 为 h5 的 commander 子实例,若是查找到,启动一个子进程按照命名规则执行 cli-test-h5 文件并带入后面的 option 参数。 这样 commander 就帮助咱们实现了多文件命令划分,咱们能够把不一样类型的执行代码放在不一样的文件中。


node ./bin/cli-test rn --type build:

同上


小结

bin 文件夹下的这3个 node 文件他们都是 commander 实例,commander 库只是一个简单的封装,帮助定义 多文件命令、执行参数 、简易文档,参数验证等。 以上就是 commander 的大体使用和我对其的理解。 源码很少,建议能够深刻学习一下。

最后

  到最后你们结合实现以上所说的 cli 和 commander,一个 commander 实现的命令行工具就能完成了,是否是很简单!?

注意

  若是执行命令发现报错为 error: xx(1) not executable. try chmod or run with root,要注意下建立的文件类型。

相关文章
相关标签/搜索