【Nodejs】326- 从零开发一个node命令行工具

本文由 IMWeb 社区授权转载自腾讯内部 KM 论坛。点击阅读原文查看 IMWeb 社区更多精彩文章。

什么是命令行工具?

命令行工具(Cmmand Line Interface)简称cli,顾名思义就是在命令行终端中使用的工具。我们常用的 gitnpmvim 等都是 cli 工具,比如我们可以通过 git clone 等命令简单把远程代码复制到本地。

为什么要用cli工具?

和 cli 相对的是图形用户界面(gui),windows 环境中几乎都是 gui 工具,而 linux 环境中则几乎都是 cli 工具,因为两者用户不同,gui 侧重于易用,cli 则侧重于效率。对于熟悉 gui 和集成开发环境(IDE)的程序员,这似乎很难理解。毕竟用鼠标点点拽拽,不是更方便么?

很遗憾,答案是否定的。gui对于某些简单操作,可能更快、更方便。比如移动文件、阅读邮件或写word文档。但如果你依赖 gui 完成全部工作,你将会错过环境的某些能力,比如使常见任务自动化,或是利用各种工具的全部功能。并且,你也无法将工具组合,创建出定制的宏工具。gui 的好处是所见即所得(what you see is what you get)。缺点是所见即全部所得(what you see is all you get)

作为注重实效的程序员,你不断的想要执行特别的操作(gui 可能不支持的操作)。当你想要快速地组合一些命令,以完成一次查询或某种其他的任务时,cli 要更为合适。比如:查看上周哪些js文件没有改动过:

如何开发一个 cli 工具?

基本上,使用任何成熟的语言都可以开发 cli 工具,作为一个前端小白,还是 JavaScript 比较顺手,因此我们选用 node 作为开发语言。

创建一个项目

安装 code 命令,运行 VS code 并打开命令面板( ⇧⌘P),然后输入 shell command 找到: Install 'code' command in PATH 就行了。

打开index.js文件,添加一段测试代码:

终端运行 node 程序,需要先输入 node 命令,比如

可以正确输出 hello world!,代码顶部的 #!/usr/bin/env node是告诉终端,这个文件要使用 node 去执行。

创建一个命令

一般 cli都有一个特定的命令,比如 git,刚才使用的 code 等,我们也需要设置一个命令,就叫 kid 吧!如何让终端识别这个命令呢?很简单,打开 package.json 文件,添加一个字段 bin,并且声明一个命令关键字和对应执行的文件:

如果想声明多个命令,修改这个字段就好了。

然后我们测试一下,在终端中输入 kid,会提示:

zsh: command not found: kid

为什么会这样呢?回想一下,通常我们在使用一个 cli 工具时,都需要先安装它,比如 vue-cli,使用前需要全局安装:

npm i vue-cli -g

而我们的 kid-cli 并没有发布到 npm 上,当然也没有安装过了,所以终端现在还不认识这个命令。通常我们想本地测试一个 npm 包,可以使用:npm link 这个命令,本地安装这个包,我们执行一下:

npm link

然后再执行

kid

命令,看正确输出 hello world! 了。

到此,一个简单的命令行工具就完成了,但是这个工具并没有任何卵用,别着急,我们来一点一点增强它的功能。

查看版本信息

首先是查看 cli 的版本信息,希望通过如下命令来查看版本信息:

kid -v

这里有两个问题

  1. 如何获取 -v 这参数?
  2. 如何获取版本信息?

在 node 程序中,通过 process.argv 可获取到命令的参数,以数组返回,修改 index.js,输出这个数组:

console.log(process.argv)

然后输入任意命令,比如:

kid -v -h -lalala

控制台会输出

这个数组的第三个参数就是我们想要的 -v

第二个问题,版本信息一般是放在package.json 文件的 version 字段中, require 进来就好了,改造后的 index.js 代码如下:

然后我们再执行kid -v,就可以输出版本号了。

初始化一个项目

接下来我们来实现一个最常见的功能,利用 cli 初始化一个项目。

整个流程大概是这样的:

  1. cd 到一个你想新建项目的目录;
  2. 执行 kid init 命令,根据提示输入项目名称;
  3. cli 通过 git 拉取模版项目代码,并拷贝到项目名称所在目录中;

为了实现这个流程,我们需要解决下面几个问题:

执行复杂的命令

上面的例子中,我们通过 process.argv 获取到了命令的参数,但是当一个命令有多个参数,或者像新建项目这种需要用户输入项目名称(我们称作“问答”)的命令时,一个简单的swith case 就显得捉襟见肘了。这里我们引用一个专门处理命令行交互的包:commander

npm i commander --save

然后改造index.js

运行

kid -h

会输出

commander已经为我们创建好了帮助信息,以及两个参数 -V-h,上面代码中的program.version 就是返回版本号,和之前的功能一致,program.parse 是将命令参数传入commander 管道中,一般放在最后执行。

添加问答操作

接下来我们添加 kid init 的问答操作,这里有需要引入一个新的包:inquirer, 这个包可以通过简单配置让 cli 支持问答交互。

npm i inquirer --save

index.js:

program.command 可以定义一个命令,description 添加一个描述,在 --help 中展示,action 指定一个回调函数执行命令。inquirer.prompt 可以接收一组问答对象,type字段表示问答类型,name 指定答案的key,可以在 answers 里通过 name 拿到用户的输入,问答的类型有很多种,这里我们使用 input,让用户输入项目名称。

运行 kid init,然后会提示输入项目名称,输入后会打印出来。

运行 shell 脚本

熟悉 git 和 linux 的同学几句话便可以初始化一个项目:

那么如何在 node 中执行 shell 脚本呢?只需要安装 shelljs 这个包就可以轻松搞定。

npm i shelljs --save

假定我们想克隆 github 上 vue-admin-template 这个项目的代码,并自动安装依赖,改造index.js,在 initAction 函数中加上执行shell脚本的逻辑:

shell.exec 可以帮助我们执行一段脚本,在回调函数中可以输出脚本执行的结果。

测试一下我们初始化功能:

cd ..kid init# 输入一个项目名称

可以看到,cli已经自动从github上拉取vue-admin-template的代码,放在指定目录,并帮我们自动安装了依赖。

切换网络代理

因为安全策略,公司对开发网有一些网络限制,比如想要直接使用 npm 安装依赖包,通常需要配置网络代理,而使用 tnpm 时则需要切换回来。每次手动切换代理实在是麻烦。因此我们将切换网络代理的功能也添加到 cli 中。

通过之前的介绍,这个功能就很简单了,通过 shelljs 可以轻松完成,index.js:

通过 kid proxykid tencent 两个命令即可轻松切换网络代理,妈妈再也不用担心我 npm 安装不上依赖了~

尾声

最后别忘了将你的 cli 工具发布到 npm (tnpm)上,给更多的同学使用。

npm publisht

怎么样,是不是感觉看似神秘的命令行开发其实也没有什么技术含量,上文列举的只是 cli 开发的冰山一角,想要开发出强大的 cli 工具,除了需要熟悉 node 和常用工具包,更重要的是了解 linux 常用命令和文件系统,希望各位同学可以受到启发,开发出属于自己的 cli 工具。

本文分享自微信公众号 - 前端自习课(FE-study)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏媒矿工厂

使用开源库构建自定义视频体验

本帖来自VES(Video Engineering Summit)2019的演讲,主要内容是使用开源库构建自定义视频体验(Building A Custom V...

9230
来自专栏架构师进阶

Java中深复制和浅复制

关于Java中的深复制、浅复制,网上也有很多资料解释,这里整理出来加入一些自己的想法。

8740
来自专栏三言两语

select标签添加onclick()事件的兼容写法

以上代码片是可以在Firefox和IE9下运行的,但是它在我的360浏览器上就是无效的,究其原因还是IE版本的问题(存在兼容性问题), 也就是:老版本只能这样 ...

90030
来自专栏学院君的专栏

Go 语言基础入门教程 —— 函数篇:匿名函数和闭包

熟悉 Laravel 框架(一个 PHP Web 框架)的同学对匿名函数应该很熟悉,Laravel 框架中有着大量匿名函数的应用场景,比如路由定义、绑定实现到接...

6710
来自专栏三言两语

JQuery基础概念知识

(本文年代久远,请谨慎阅读)JQuery是继prototype之后又一个优秀的Javascript库。它是轻量级的js库 ,它兼容CSS3,还兼容各种浏览器(I...

9810
来自专栏三言两语

添加轻量的RSS订阅内容阅读展示支持

大部分资讯类的网站都提供RSS订阅功能,其一般是是XML格式的网页信息,目的是为了能够方便第三方站点轻松的获取本站的最新内容,在Hexo博客中一般也会包含see...

9920
来自专栏Code思维奇妙屋

浅入深出Vue:代码整洁之封装

这句话来自20世纪中期注明现代建筑大师 路德维希·密斯·范·德·罗所说,他秉承的是少即是多的建筑设计哲学,玻璃幕墙等现代建筑结构便是由他缔造的。

7220
来自专栏SmartSi

Hive Grouping Sets,CUBE与ROLLUP

这篇文章描述了 SELECT 语句 GROUP BY 子句的增强聚合功能 GROUPING SETS。GROUPING SETS 子句是 SELECT 语句的 ...

33220
来自专栏品茗IT

SpringBoot入门建站全系列(七)日志组件的使用

前面六篇已经对SpringBoot的基础用做了介绍,日常项目使用已经足够,本篇介绍下SpringBoot日志使用的注意事项。

16020
来自专栏三言两语

J2EE前后台传值带中文时乱码

(本文年代久远,请谨慎阅读)传值乱码问题在Web开发中涉及许多方面:登陆注册时,是否正确得到正确的中文用户名;修改信息时,是否可以显示提交的中文信息;

9230

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励