日常开发中,遇到的各种命令行工具(cli), 如 vue init webpack xxx
, 很多是通过node.js开发的。node.js cli本质其实就是执行node脚本。
可执行脚本
首先使用JavaScript编写一个可执行脚本 hello
#!/usr/bin/env node
console.log('hello world');
然后修改 hello文件的权限 $ chmod 755 hello
,此时就可以执行 hello 了。
$ ./hello
hello world
如果想把 hello 前面的路径去除,需要将 hello 的路径加入到环境变量 PATH。有一种更好的做法就是 使用 npm link
。
在当前目录下新建 package.json ,写入下面的内容。
{
"name": "hello-cli",
"bin": {
"hello": "hello"
}
}
然后执行 npm link 命令,输出
可见,npm link
创建了2个软链接分别放到系统环境变量$PATH目录里,hello命令和hello-cli模块。
npm link在用户使用的场景下是不需要执行的,用户使用npm i -g hello-cli命令安装即可。
此时再次执行 hello 时就不用输入路径了。
hello
hello world
最后通过 npm publish
发布到npm官网,就可以供大家安装使用了。
package.json
scripts
指定了运行脚本命令的npm命令行缩写,比如start指定了运行npm run start时,所要执行的命令。
下面的设置指定了npm run preinstall、npm run postinstall、npm run start、npm run test时,所要执行的命令。
"scripts": {
"preinstall": "echo here it comes!",
"postinstall": "echo there it goes!",
"start": "node index.js",
"test": "tap test/*.js"
}
bin
项用来指定各个内部命令对应的可执行文件的位置。
"bin": {
"someTool": "./bin/someTool.js"
}
上面代码指定,someTool 命令对应的可执行文件为 bin 子目录下的 someTool.js。Npm会寻找这个文件,在node_modules/.bin/目录下建立符号链接。
在上面的例子中,someTool.js会建立符号链接node_modules/.bin/someTool。由于node_modules/.bin/目录会在运行时加入系统的PATH变量,
因此在运行npm时,就可以不带路径,直接通过命令来调用这些脚本。
命令行参数
原生progress.argv
命令行参数可以用系统变量 process.argv
获取。
#!/usr/bin/env node
console.log('hello ', process.argv[2]);
执行时,直接在脚本文件后面,加上参数即可。
$ ./hello ben
hello ben
其中process为node进程中的全局变量,process.argv为一数组,数组内存储着命令行的各个部分,
argv[0]为node的安装路径,argv[1]为主模块文件路径,剩下为子命令或参数,如下:
$ hello a b c
# process.argv的值为[ '/usr/local/bin/node', '/Users/benyasin/Downloads/hello', 'a', 'b', 'c' ]
除了上面原始方式之外,还有几个比较流行的工具,比如yargs
与commander
。
yargs
示例:
#!/usr/bin/env node
var argv = require('yargs')
.alias('n', 'name')
.argv;
console.log('hello ', argv.n);
可以通过空格
或=
的方式进行赋值。还可以使用alias指定别名。
$ hello -n tom
hello tom
$ hello --name tom
hello tom
$ hello --name=tom
hello tom
commander
#!/usr/bin/env node
const program = require('commander');
program
.option('-n, --name', 'your name', 'ben')
.parse(process.argv);
console.log('hello', program.name);
执行命令行程序的方式如下:
$ hello -n tom
hello tom
$ hello --name
hello ben
用法:.option('-n, --name <name>', 'description info', 'default value')
- 第一个参数 自定义标志<必须>:分为长短标识,中间用逗号、竖线或者空格分割;标志后面可跟参数,可以用<> 或者 []修饰,前者意为必须参数,后者意为可选参数
- 第二个参数 选项描述<省略不报错>:在使用 --help 命令时显示标志描述
- 第三个参数 选项参数默认值,可选。
除了option
外,还有几个常用的api:version
、command
、parse
、outputHelp
。更多使用细节请参照其官方文档。
其它依赖模块
除了上述的yargs
或commander
外,编写命令行工具通常还会用到其它一些第三方模块,比如:
inquirer
: 命令行交互界面集合。可以提问,解析输入,校验回答等ora
: 可以使终端输出更优雅,设置正在进行,成功或失败chalk
: 可以对终端输出的文字设置一些颜色等样式
有时还会用到download-git-repo
,比如开发一个脚本架工具,在初始化项目时,需要实时地从github上拉取最新的项目模板。
这里推荐一个智能合约的脚本架工具robin,有兴趣的同学可以参考一下它的实现。