"私人定制"CLI工具

"私人定制"CLI工具

1 前言

本文面向有JavaScript开发经验且希望入门CLI工具制作的开发人员。文中没有太多理论知识,通过从0到1开发一个简单的CLI工具,分享了CLI工具的开发、发布流程。看完本文,你可以学会如何"私人定制"一个CLI工具来提升开发效率。

总之,简单,实用,有趣。

2 什么是CLI工具

维基百科是这样定义CLI的

CLI(Command-line Interface),命令行界面,指通过输入字符组成的命令行进行操作的用户界面。

常见的CLI工具举例

  • 系统命令:cd、mkdir、ifconfig
  • 脚手架工具:create-react-app、vue-cli、yeoman
  • 预处理器:less、sass、babel
  • 测试工具:mocha、karma、wrk
  • 构建工具:webpack、gulp、grunt

3 为什么要自己开发CLI工具

举例,如果想查看本机ip地址,通常我会使用ifconfig命令,如图

ifconfig执行结果


执行命令后系统输出了一堆信息,从这堆信息中找到ip地址大概需要花5秒时间,这不是我想要的。我希望的结果是这样的

ip执行结果

输入ip命令,直接得到本机ip地址,1秒搞定,简单快捷。

理想很丰满,现实很骨感。真实的情况是:当我在终端敲下ip命令时,系统给我的反馈是"command not found:ip"。

ip执行结果

很显然,系统并没有为我提供ip这个命令,所以我们需要自己来实现它。

4 开始开发CLI工具

4.1 第一步:确保安装了Node.js

node版本检测

4.2 第二步:编写脚本、测试运行

创建文件夹ip-cli,并新建index.js

mkdir ip-cli && cd ip-cli
touch index.js

编辑index.js,内容如下

const os = require("os")
const ip = os.networkInterfaces().en0[1].address
const options = process.argv.slice(2)
if (options[0] === '-v') {
    console.log('v1.0.0')
} else {
    console.log(`your ip is: ${ip}`)
}

执行node index.js,结果如下

执行脚本

4.3 声明执行环境

执行which env

which env

将返回的结果设置到index.js脚本的第一行,并指定脚本的解释器为node

然后,代码就变成了下面这样

#!/usr/bin/env node

const os = require("os")
const ip = os.networkInterfaces().en0[1].address
const options = process.argv.slice(2)
if (options[0] === '-v') {
    console.log('v1.0.0')
} else {
    console.log(`your ip is: ${ip}`)
}

代码中的#!被称为Shebang,是用来告诉编译器用什么命令执行文件的。Shebang的一些具体用法罗列如下

1、如果脚本文件中没有#!这一行,那么执行时会默认采用当前Shell去解释这个脚本(即:$SHELL环境变量)。
2、如果#!之后的解释程序是一个可执行文件,那么执行这个脚本时,它就会把文件名及其参数一起作为参数传给那个解释程序去执行。
3、如果#!指定的解释程序没有可执行权限,则会报错“bad interpreter: Permission denied”。如果#!指定的解释程序不是一个可执行文件,那么指定的解释程序会被忽略,转而交给当前的SHELL去执行这个脚本。
4、如果#!指定的解释程序不存在,那么会报错“bad interpreter: No such file or directory”。注意:#!之后的解释程序,需要写其绝对路径(如:#!/bin/bash),它是不会自动到$PATH中寻找解释器的。
5、当然,如果你使用类似于”bash test.sh”这样的命令来执行脚本,那么#!这一行将会被忽略掉,解释器当然是用命令行中显式指定的bash。
6、脚本文件必须拥有可执行权限。

env可以在系统的PATH目录中查找脚本解释器安装目录。

添加#!/usr/bin/env node是告诉系统,这个脚本使用Node.js来执行。这样我们就可以简化命令,执行index.js直接得到ip地址,不需要显式的调用node index.js

4.4 分配执行权限

在我们直接执行index.js后,结果如下

执行index.js

我们并没有得到ip地址,查看文件发现index.js没有可执行权限。

查看文件

执行chmod +x index.js给脚本分配可执行权限,然后index.js就可以执行了。

增加可执行权限

4.5 设置环境变量

执行$PATH查看环境变量

PATH

可以看到我电脑的PATH变量为

/usr/local/mongodb/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:“/usr/libexec/java_home”::/platform-tools:/tools:“/usr/libexec/java_home”:“/usr/libexec/java_home”/bin:/usr/local/mongodb/bin:/Users/qianxuemin/bin/

这里usrUnix System Resource,而不是User, 通常/usr/bin下面的都是系统预装的可执行程序,会随着系统升级而改变, /usr/local/bin目录是给用户放置自己的可执行程序的地方,推荐放在这里,不会被系统升级而覆盖同名文件。如果两个目录下有相同的可执行程序,谁优先执行受到PATH环境变量的影响,比如我的电脑PATH变量中,/usr/local/bin优先于/usr/bin

接下来执行ln index.js /usr/local/bin/ip,创建index.js的一个硬链接文件/usr/local/bin/ip,如图

ln

然后就可以直接使用自定义的命令ip了 。

ip

如果只是自己使用,那么一个简单的CLI工具就开发完毕了,但作为一名喜欢分享的童鞋,我希望可以把这个CLI工具分享给他人。

这时候可以选择使用npm管理项目,与他人共享开发成果。为了演示在npm项目中开发CLI工具的流程,我们暂时先删除刚才的命令ip

删除命令

5 使用npm 管理项目

5.1 初始化npm项目

执行npm init -y将项目初始化为npm项目

初始化

目录结构如下

.
├── index.js
└── package.json

5.2 设置bin字段

package.json中设置bin字段,定义一个命令ip并指定该命令执行./index.js文件

{
  "name": "ip-cli",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "bin": {
    "ip": "./index.js"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

5.3 测试CLI工具

执行npm link ,在全局的 node_modules下生成一个符号链接

npm link

然后就可以在全局使用 package.jsonbin 字段定义的命令ip了,执行ip命令结果如下

ip

输出了本机IP地址,测试成功。

5.4 发布CLI工具

执行npm login 登录账号,登录成功后执行npm whoami可以看到npm用户名

npm whoami

确认无误后执行 npm publish发布npm包。

发布包的时候需要确保包的名字没有被占用,如下是包名冲突的情况

npm publish

遇到这种情况可以选择换个名字或者加scope,如下我给包名增加scope,将包名由ip-cli改为@qianxuemin/ip-cli

{
  "name": "@qianxuemin/ip-cli",
  "version": "1.0.2",
  "description": "",
  "main": "index.js",
  "bin": {
    "ip": "./index.js"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

加了scope后,发布包的时候需要设置访问级别,我们设置为公开

npm publish

发布成功后,其他开发人员就可以通过npm install @qianxuemin/ip-cli -g 安装并使用了。

ip

至此,一个简单的CLI工具开发、发布完成。

6 注意事项

上面演示了CLI工具的整体开发、发布流程,没有对细节做太多说明。如下总结了在日常开发CLI工具中需要注意的事项与大家分享:

1、工具应当包含完整的README.md

2、工具应当包含helpversion信息

3、工具应当包含运行指示,比如加载中

4、发布之前要到 https://www.npmjs.com 查找一下自己的包名有没有被占用,如果被占用需要先修改包名或加scope

5、发布包时,可以通过在项目中添加.npmignore.gitignore文件限制发布的文件内容,.npmignore优先级高于.gitignore。或者在 package.jsonfiles 字段设置发布哪些文件或目录,它的优先级高于 .npmignore.gitignore

7 开发CLI工具常用的库

相比原生Node.js,使用一些开源模块能够简化CLI工具的开发,提高开发效率。如下是开发CLI工具常用的模块:

  • commander: 注册、解析命令行参数
  • Inquirer: 让命令行与用户进行交互
  • chalk: 给命令行字符加颜色
  • shelljs: 跨平台调用shell命令的node封装
  • Ora: 命令行提示图标
  • progress: 命令行进度条
  • blessed-contrib: 命令行可视化组件
  • download-git-repo:拉取git仓库源代码

由于文章篇幅有限,这里就不一一进行介绍了,使用时可以直接到 npmjs.com/搜索。

8 总结

本文通过一个简单的例子分享了CLI工具的开发、发布流程,并总结了一些开发注意事项和常用的库。旨在帮助JavaScript开发人员入门CLI工具开发。希望大家都能在掌握CLI工具开发流程后,发挥想象力,开发出一些实用、好玩的东西,让我们的开发变得高效、有趣。

9 参考资料

发布于 2019-09-27 11:06