• commanderJs编写命令行工具(cli)


    前言:

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

    新手误区:

      在开始实现之前,我知道有 commander 这个node库,有很多cli使用了它,对它的大致了解就是它可以帮助开发者简化实现命令流程。  于是最初以为不利用库要去实现命令行会很麻烦,commander 帮我们解决了各种兼容问题等等。  后来发现并不是的,没有commander 我们也可以不用化多大力气实现命令行,commander 仅仅是一个本身也不太复杂的封装(源码也只有1200行)。  我们的第一步还是应该先搞清楚,通过npm如何实现命令行。
     

    一个简单的cli:

    bin:

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

    /*cli-test 的 package.json*/
    "bin": {
        "cli-test": "./bin/cli-test"
    }

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

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

    cli文件:

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

    #!/usr/bin/env node
    
    //do something

    好了,到这里我们的cli就完成了。  其实有很多三方cli也并没有用类似 commandernode 库,如果我们的 cli 足够简单,以上这样就可以了。  下面接着讲 commander

     

    commander:

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

    #!/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 封装了一些也不算很复杂的功能。 

    再来一个例子:

    新建了两个文件,要以 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 里查找是否有 nameh5commander 子实例,如果查找到,启动一个子进程按照命名规则执行 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,要注意下创建的文件类型。

  • 相关阅读:
    redis(二)高级用法
    redis(一) 安装以及基本数据类型操作
    RabbitMQ(五) -- topics
    JS实时数据运算
    Access数据库中Sum函数返回空值(Null)时如何设置为0
    asp检测数字类型函数
    MVC:从客户端中检测到有潜在危险的 Request.Form 值 的解决方法
    WIN8系统安装软件时提示"扩展属性不一致"的解决方法
    免费的网络扫描器-Advanced IP Scanner
    中国电信大亚DP607光猫破解,设置路由,wifi!关闭远程管理,改连接限制,SN码查询!
  • 原文地址:https://www.cnblogs.com/1wen/p/10142210.html
Copyright © 2020-2023  润新知