背景

项目中需要使用 postinstall 脚本来完成执行 npm install 之后的任务,而如果需要在安装本身之后自动安装相关工具包,又不想作为依赖放在 package.json 中,就可以在 postinstall 脚本中执行 install 的命令,从而达到目的。

API

Node 提供了 child_process 的 API,可以新开一个子进程来执行 shell 命令,具体用法参考官方文档

exec() vs execSync()

exec() 方法最多可以接受两个参数,第一个参数是所要执行的 shell 命令,第二个参数是回调函数,该函数接受三个参数,分别是发生的错误、标准输出的显示结果、标准错误的显示结果。

1
2
3
4
5
6
7
8
9
10
11
12
var exec = require('child_process').exec;
var child = exec('ls -l');

child.stdout.on('data', function(data) {
console.log('stdout: ' + data);
});
child.stderr.on('data', function(data) {
console.log('stdout: ' + data);
});
child.on('close', function(code) {
console.log('closing code: ' + code);
});

子进程本身有 close 事件,可以设置回调函数。监听 data 事件以后,可以实时输出结果,否则只有等到子进程结束,才会输出结果。

execSyncexec 的同步执行版本。它可以接受两个参数,第一个参数是所要执行的命令,第二个参数用来配置执行环境。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var execSync = require("child_process").execSync;

var SEPARATOR = process.platform === 'win32' ? ';' : ':';
var env = Object.assign({}, process.env);

env.PATH = path.resolve('./node_modules/.bin') + SEPARATOR + env.PATH;

function myExecSync(cmd) {
var output = execSync(cmd, {
cwd: process.cwd(),
env: env
});

console.log(output);
}

myExecSync('eslint .');

上面代码中,execSync 方法的第二个参数是一个对象。该对象的 cwd 属性指定脚本的当前目录,env 属性指定环境变量。上面代码将 ./node_modules/.bin 目录,存入 $PATH 变量。这样就可以不加路径,引用项目内部的模块命令了,比如 eslint 命令实际执行的是 ./node_modules/.bin/eslint

execSync 的返回对象不为 ChildProcess,所以不能设置回调函数,也无法实时输出结果和监听报错。

ora & chalk

在涉及命令行交互操作时,有两个好用的 npm 库:orachalk,可以实现 loading 效果和改变命令行输出颜色等。