我们在child_process中创建的进程就是Node.js的子进程;
child_process的用法:
异步:
- exec
- execFile
- fork
- spawn
同步:
- execSync
- execFileSync
- spawnSync
js
const cp = require("child_process");
child_process打印出的结果如下, 它提供了以下多种api:
异步方法
exec
exec主要是用来执行shell脚本的,exec支持3个参数command,options,callback;otpions有如下一些变量;
cwd的用法,指向上级路径,因为上级路径没有node_modules,所以不会打印出任何信息:
js
cp.exec(
path.resolve(__dirname, "test.shell"),
{
cwd: path.resolve("../"),
},
function (err, stdout, stderr) {
console.log(err);
console.log(stdout);
console.log(stderr);
}
);
js
`child_process.exec(command, [options], [callback])`
我们可以通过ls
打印当前文件夹的信息;
js
// 用来执行shell脚本的
cp.exec("ls -al", function (err, stdout, stderr) {
console.log(err);
console.log(stdout);
console.log(stderr);
});
err表示异常,stdout是正常执行的输出结果,stderr是异常时的输出结果;
exec加上管道符grep
打印出node_modules
的文件记录;
js
// 管道符, 筛选出node_modules
cp.exec("ls -al|grep node_modules", function (err, stdout, stderr) {
console.log(err);
console.log(stdout);
console.log(stderr);
});
exec
也支持执行文件, 但不支持传参;
js
// exec也支持执行文件,但不支持传参
cp.exec(path.resolve(__dirname, "test.shell"), function (err, stdout, stderr) {
console.log(err);
console.log(stdout);
console.log(stderr);
});
execFile
execFile是用来执行一个文件;下面的例子ls
也是指向一个文件bin/ls
, execFile第二个参数是数组["-al"]
,它的回调函数和exec相同的;
js
cp.execFile("ls", ["-al"], function (err, stdout, stderr) {
console.log(err);
console.log(stdout);
console.log(stderr);
});
execFile中不能直接使用管道符grep
,正确打开方式是shell脚本中写grep
; 新建一个test.shell
文件,echo $1
是打印第一个参数;
js
ls -al|grep node_modules
echo $1
js
// __dirname是绝对路径
cp.execFile(
path.resolve(__dirname, "test.shell"),
["-al"],
function (err, stdout, stderr) {
console.log(err);
console.log(stdout);
console.log(stderr);
}
);
这时会报错,对test.shell文件得加上可执行权限;
js
chmod =x bin/process/test.shell
ll bin/process/test.shell
spawn
spawn支持的参数file, args入参,options;spawn没有回调;
js
`child_process.spawn(command, [args], [options])`
spawn实现回调和打印报错;
js
const child = cp.spawn(path.resolve(__dirname, "test.shell"), ["-al"], {
cwd: path.resolve(".."),
});
child.stderr.on("data", function (chunk) {
console.log("stderr--->", chunk.toString());
});
child.stdout.on("data", function (chunk) {
console.log("stdout-->", chunk.toString());
});
spawn和exec、execFile的区别
spawn和exec、execFile都是异步执行;区别是:spawn是流式执行方式,它更适合耗时的执行任务,比如:npm的install,需要不断打印日志的;exec、execFile更是和开销比较小的任务;
js
cnpm i -S urllib axios pkg-dir path-exists fs-extra commander npminstall user-home yargs
spawn执行cnpm install
,一行行打印执行结果:
js
const child = cp.spawn("cnpm", ["install"], {
cwd: path.resolve("/Users/Minjie/Documents/vue3/mj-cli-new"),
});
child.stderr.on("data", function (chunk) {
console.log(chunk.toString());
});
child.stdout.on("data", function (chunk) {
console.log(chunk.toString());
});
用exec实现cnpm install,一次性打印出来所有结果:
js
cp.exec(
"cnpm install",
{ cwd: path.resolve("/Users/Minjie/Documents/vue3/mj-cli-new") },
function (err, stdout, stderr) {
console.log(err);
console.log(stdout);
console.log(stderr);
}
);
fork
fork使用Node执行命令;
创建一个child.js文件;
js
console.log("child process");
console.group("child pid:", process.pid);
类似于require;
js
cp.fork(path.resolve(__dirname, "child.js"));
console.log("main pid:", process.pid);
fork和require的区别是主进程的pid和child pid不同,fork会启动子进程;
主进程和子进程可以进行通信;
js
const child = cp.fork(path.resolve(__dirname, "child.js"));
child.send("hello child process", () => {
// 断开
child.disconnect();
});
console.log("main pid:", process.pid);
child.js
js
console.log("child process");
console.group("child pid:", process.pid);
// 监听
process.on("message", (msg) => {
console.log("msg:", msg);
});
fork适用于耗时的操作,比如下载文件;
同步方法
execSync、execFileSync
execSync
打印结果不再需要回调函数,使用简单,适用于简单命令;execSync
有一个隐患就是对命令的安全没有做校验;
js
const ret = cp.execSync("ls -al|grep node_modules");
console.log("ret:", ret.toString());
const ret2 = cp.execFileSync("ls", ["-al"]);
console.log("ret2:", ret2.toString());
const ret3 = cp.spawnSync("ls", ["-al"]);
console.log("ret3:", ret3.output.toString());