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
命令,如图
执行命令后系统输出了一堆信息,从这堆信息中找到ip地址大概需要花5秒时间,这不是我想要的。我希望的结果是这样的
输入ip
命令,直接得到本机ip地址,1秒搞定,简单快捷。
理想很丰满,现实很骨感。真实的情况是:当我在终端敲下ip
命令时,系统给我的反馈是"command not found:ip"。
很显然,系统并没有为我提供ip
这个命令,所以我们需要自己来实现它。
4 开始开发CLI工具
4.1 第一步:确保安装了Node.js
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
将返回的结果设置到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
后,结果如下
我们并没有得到ip地址,查看文件发现index.js
没有可执行权限。
执行chmod +x index.js
给脚本分配可执行权限,然后index.js
就可以执行了。
4.5 设置环境变量
执行$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/
这里usr
指Unix 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
,如图
然后就可以直接使用自定义的命令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
下生成一个符号链接
然后就可以在全局使用 package.json
中 bin
字段定义的命令ip
了,执行ip
命令结果如下
输出了本机IP地址,测试成功。
5.4 发布CLI工具
执行npm login
登录账号,登录成功后执行npm whoami
可以看到npm用户名
确认无误后执行 npm publish
发布npm包。
发布包的时候需要确保包的名字没有被占用,如下是包名冲突的情况
遇到这种情况可以选择换个名字或者加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 install @qianxuemin/ip-cli -g
安装并使用了。
至此,一个简单的CLI工具开发、发布完成。
6 注意事项
上面演示了CLI工具的整体开发、发布流程,没有对细节做太多说明。如下总结了在日常开发CLI工具中需要注意的事项与大家分享:
1、工具应当包含完整的README.md
2、工具应当包含help
和version
信息
3、工具应当包含运行指示,比如加载中
4、发布之前要到 https://www.npmjs.com 查找一下自己的包名有没有被占用,如果被占用需要先修改包名或加scope
5、发布包时,可以通过在项目中添加.npmignore
或.gitignore
文件限制发布的文件内容,.npmignore
优先级高于.gitignore
。或者在 package.json
中 files
字段设置发布哪些文件或目录,它的优先级高于 .npmignore
和 .gitignore
。
7 开发CLI工具常用的库
相比原生Node.js,使用一些开源模块能够简化CLI工具的开发,提高开发效率。如下是开发CLI工具常用的模块:
- commander: 注册、解析命令行参数
- Inquirer: 让命令行与用户进行交互
- chalk: 给命令行字符加颜色
- shelljs: 跨平台调用shell命令的node封装
- Ora: 命令行提示图标
- progress: 命令行进度条
- blessed-contrib: 命令行可视化组件
- download-git-repo:拉取git仓库源代码
由于文章篇幅有限,这里就不一一进行介绍了,使用时可以直接到 https://www.npmjs.com/搜索。
8 总结
本文通过一个简单的例子分享了CLI工具的开发、发布流程,并总结了一些开发注意事项和常用的库。旨在帮助JavaScript开发人员入门CLI工具开发。希望大家都能在掌握CLI工具开发流程后,发挥想象力,开发出一些实用、好玩的东西,让我们的开发变得高效、有趣。
9 参考资料
- npm官网:https://www.npmjs.com.cn/
- Shebang:https://zh.wikipedia.org/wiki/Shebang
- Shebang:https://blog.csdn.net/u012294618/article/details/78427864
- awesome-nodejs:https://github.com/sindresorhus/awesome-nodejs#command-line-apps