child_process

const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']); ls.stdout.on('data', (data) => {
console.log(`输出:${data}`);
}); ls.stderr.on('data', (data) => {
console.log(`错误:${data}`);
}); ls.on('close', (code) => {
console.log(`子进程退出码:${code}`);
}); 默认情况下,Node.js 的父进程与衍生的子进程之间会建立 stdin、stdout 和 stderr 的管道。

child_process.exec

衍生一个 shell 并在 shell 中执行 command,并缓冲产生的输出。 command 会被 shell 直接执行,所以 command 中的特殊字符(因shell而异)需要相应的处理:
exec('"/path/to/test file/test.sh" arg1 arg2');
// 使用双引号,则路径中的空格不会被当成多个参数。 exec('echo "The \\$HOME variable is $HOME"');
// 第一个 $HOME 会被转义,第二个则不会。
如果指定了 callback,则调用时会传入参数 (error, stdout, stderr)。 当成功时,error 会是 null。 当出错时,error 会是 Error 实例。 error.code 属性是子进程的退出码,error.signal 是终止进程的信号。 任何非 0 的退出码都会被视为出错。

传给 callback 的 stdout 和 stderr 包含子进程的输出。 默认情况下,Node.js 会将输出解码成 UTF-8 字符串。 encoding 用于指定解码 stdout 和 stderr 的字符编码。 如果 encoding 是 'buffer' 或无效的字符编码,则传入 callback 的会是 Buffer。

const { exec } = require('child_process');
exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => {
if (error) {
console.error(`执行出错: ${error}`);
return;
}
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
});
如果 timeout 大于 0,则当子进程运行超过 timeout 毫秒时,父进程就会发送带 killSignal 属性(默认为 'SIGTERM')的信号。

不像 POSIX 中的 exec(3), child_process.exec() 不会替换现有的进程,且使用 shell 来执行命令。

如果调用此方法的 util.promisify() 版本,则返回的 Promise 会返回具有 stdout 属性和 stderr 属性的对象。

const util = require('util');
const exec = util.promisify(require('child_process').exec); async function lsExample() {
const { stdout, stderr } = await exec('ls');
console.log('stdout:', stdout);
console.log('stderr:', stderr);
}
lsExample();

child_process.spawn

运行 ls -lh /usr,并捕获 stdout、stderr、以及退出码:

const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']); ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
}); ls.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
}); ls.on('close', (code) => {
console.log(`子进程退出码:${code}`);
}); 运行 ps ax | grep ssh:
const { spawn } = require('child_process');
const ps = spawn('ps', ['ax']);
const grep = spawn('grep', ['ssh']); ps.stdout.on('data', (data) => {
grep.stdin.write(data);
}); ps.stderr.on('data', (data) => {
console.log(`ps stderr: ${data}`);
}); ps.on('close', (code) => {
if (code !== 0) {
console.log(`ps 进程退出码:${code}`);
}
grep.stdin.end();
}); grep.stdout.on('data', (data) => {
console.log(data.toString());
}); grep.stderr.on('data', (data) => {
console.log(`grep stderr: ${data}`);
}); grep.on('close', (code) => {
if (code !== 0) {
console.log(`grep 进程退出码:${code}`);
}
});
const { spawn } = require('child_process');

const subprocess = spawn(process.argv[0], ['child_program.js'], {
detached: true,
stdio: 'ignore'
}); subprocess.unref();

fork

衍生一个新的 Node.js 进程,并通过建立一个 IPC 通讯通道来调用一个指定的模块,该通道允许父进程与子进程之间相互发送信息
fork方法返回一个隐式创建的代表子进程的ChildProcess对象
子进程的输入/输出操作执行完毕后,子进程不会自动退出,必须使用process.exit()方法显式退出 args 运行该文件模块文件时许哟啊使用的参数
options 选项对象
cwd 指定子进程当前的工作目录
env 属性值为一个对象,用于以"键名/键值"的形式为子进程指定环境变量
encoding 属性值为一个字符串,用于指定输出及标准错误输出数据的编码格式,默认值为'utf8'
silent 属性值为布尔值,当属性值为false时,子进程和父进程对象共享标准(输入/输出),true时不共享 child.send(message,[sendHandle]);//在父进程中向子进程发送消息
process.send(message,[sendHandle]);//在子进程中向主进程发送消息 let {
fork
} = require('child_process');
let path = require('path');
let child = fork(path.join(__dirname, 'fork.js'));
child.on('message', function (m) {
console.log('父进程接收到消息:', m);
process.exit();
});
child.send({
name: 'zfpx'
});
child.on('error', function (err) {
console.error(arguments);
}); //fork.js
process.on('message', function (m, setHandle) {
console.log('子进程收到消息:', m);
process.send({
age: 9
});
})
在默认情况下子进程对象与父进程对象共享标准输入和标准输出。如果要让子进程对象用独立的标准输入输出,可以将silent属性值设置为 true forkslient.js

let {
fork
} = require('child_process');
let path = require('path'); let p1 = fork('node', [path.join(__dirname, 'fork1.js')], {
silent: true
});
let p2 = fork('node',path.join(__dirname, 'fork2.js'));
p1.stdout.on('data', function (data) {
console.log('子进程1标准输出:' + data);
p2.send(data.toString());
});
p1.on('exit', function (code, signal) {
console.log('子进程退出,退出代码为:' + code);
});
p1.on('error', function (err) {
console.log('子进程开启失败:' + err);
process.exit();
}); //fork1.js
process.argv.forEach(function (item) {
process.stdout.write(item + '\r\n');
}); //fork2.js
let fs = require('fs');
let out = fs.createWriteStream(path.join(__dirname, 'msg.txt'));
process.on('message', function (data) {
out.write(data);
});

exec

exec方法可以开启一个用于运行某个命令的子进程并缓存子进程的输出结果
spawn是一个异步方法,exec是一个同步方法
衍生一个 shell 并在 shell 上运行命令
child_process.exec(command,[options],[callback]);
command 需要执行的命令
options 选项对象
cwd 子进程的当前工作目录
env 指定子进程的环境变量
encoding 指定输出的编码
timeout 子进程的超时时间
maxbuffer 指定缓存标准输出和错误输出的缓存区最大长度
killSignal 指定关闭子进程的信号,默认值为 "SIGTERM"
callback 指定子进程终止时调用的回调函数
function(err,stdout,stderr){}
err 错误对象
stdout 标准输出
stderr 错误输出 let {
exec
} = require('child_process');
let path = require('path');
let p1 = exec('node test1.js a b c', {
cwd: path.join(__dirname, 'test3')
}, function (err, stdout, stderr) {
if (err) {
console.log('子进程开启失败:' + err);
process.exit();
} else {
console.log('子进程标准输出\r\n' + stdout.toString());
p2.stdin.write(stdout.toString());
}
});
let p2 = exec('node test2.js', {
cwd: path.join(__dirname, 'test3')
}, function (err, stdout, stderr) {
process.exit();
});

execFile

可以使用execFile开启一个专门用于运行某个可执行文件的子进程
类似 child_process.exec(),但直接衍生命令,且无需先衍生一个 shell child_process.execFile(file,[args],[optioins],[callback]); file 指定需要运行的可执行文件路径及文件名
args 运行该文件所需要的参数
options 开启子进程的选项
callback 指定子进程终止时调用的回调函数 let {
execFile
} = require('child_process');
let path = require('path'); let p1 = execFile('node', ['./test1.js'], {
cwd: path.join(__dirname, 'test4')
}, function (err, stdout, stderr) {
if (err) {
console.log('子进程1开启失败:' + err);
process.exit();
} else {
console.log('子进程标准输出:' + stdout.toString());
p2.stdin.write(stdout.toString());
}
});
let p2 = execFile('node', ['./test2.js'], {
cwd: path.join(__dirname, 'test4')
}, function (err, stdout, stderr) {
if (err) {
console.log('子进程2开启失败:' + err);
process.exit();
} else {
console.log('子进程标准输出:' + stdout.toString());
}
});

cluster

为了利用多核CPU的优势,Node.js提供了一个cluster模块允许在多个子进程中运行不同的Node.js应用程序。

fork方法创建work对象

可以使用fork方法开启多个子进程,在每个子进程中创建一个Node.js应用程序的实例,并且在该应用程序中运行一个模块文件。
fork方法返回一个隐式创建的work对象
在cluster模块中,分别提供了一个isMaster属性与一个isWork属性,都是布尔值
cluster.fork([env]);

获取所有的worker

for(let index in cluster.workers){
console.log(cluster.workers[index]);
}

获取当前的worker和id

if(cluster.isMaster){
cluster.fork()
}else if(cluster.isWorker){
console.log('I am worker #'+cluster.worker.id);
}

服务器

let cluster = require('cluster');
let http = require('http');
if (cluster.isMaster) {
cluster.fork();
console.log('这段代码运行在主进程里');
} else {
http.createServer(function (req, res) {
if (req.url != '/favicon.ico') {
res.end('hello');
console.log('这段代码运行在子进程里');
}
}).listen(8080);
}
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length; if (cluster.isMaster) {
console.log(`主进程 ${process.pid} 正在运行`); // 衍生工作进程。
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
} cluster.on('exit', (worker, code, signal) => {
console.log(`工作进程 ${worker.process.pid} 已退出`);
});
} else {
// 工作进程可以共享任何 TCP 连接。
// 在本例子中,共享的是 HTTP 服务器。
http.createServer((req, res) => {
res.writeHead(200);
res.end('你好世界\n');
}).listen(8000); console.log(`工作进程 ${process.pid} 已启动`);
}
05-20 20:19