nodejs学习笔记(三)子进程
本文由 小茗同学 发表于 2017-06-15 浏览(5136)
最后修改 2017-06-22 标签:nodejs

概述

中文文档:http://nodejs.cn/api/child_process.html

利用nodejs的子进程模块(child_process)我们可以轻松调用外部程序、命令等。

API

一般习惯用异步方式调用,子进程共有4个异步方法:

  • child_process.exec()
  • child_process.execFile()
  • child_process.spawn()
  • child_process.fork()

需要知道的是,exec()execFile()fork()3个方法底层都是通过.spawn()实现的,所以spawn()才是终极玩法。

child_process.exec()

衍生一个 shell,然后在 shell 中执行 command,且缓冲任何产生的输出。

child_process.exec(command[, options][, callback])
  • command:要运行的命令以及参数,命令和参数之间用空格分隔,如nginx -s reload
  • options:可选的选项,常见有:encodingmaxBuffer等,具体看文档;
  • callback:回调,有3个参数:error, stdout, stderr,会在子进程完全结束时才会触发;

execexecFilespawn最大的区别是前面2个有回调函数,后者没有,exec(包括execFile,下同)会把所有输出合并起来,然后等结束时一次性输出,而spawn可以实时监听输出,为了防止输出数据太大,所以exec有一个maxBuffer参数。二者另外一个区别就是编码的问题,具体后面有例子讲解。

示例:

//  重新加载nginx配置文件
const {exec} = require('child_process');
execFile('nginx -s reload', (error, stdout, stderr) => {
	if(error) {
		console.error(`exec error: ${error}`);
		return;
	}
	console.log(`${stdout}`);
	console.log(`${stderr}`);
});

child_process.execFile()

execFileexec很类似,二者的主要区别是:

  • execFile不用先衍生一个 shell,它指定可执行的file直接衍生为一个新进程,这使得它比exec()更高效;
  • execFile将命令和参数分开,exec是合在一起的。

语法:

child_process.execFile(file[, args][, options][, callback])

示例:

//  重新加载nginx配置文件
const {execFile} = require('child_process');
execFile('nginx', ['-s', 'reload'], (error, stdout, stderr) => {
	if(error) {
		console.error(`exec error: ${error}`);
		return;
	}
	console.log(`${stdout}`);
	console.log(`${stderr}`);
});

对于回调函数,默认情况下,Node.js 会解码输出为 UTF-8,并将字符串传给回调。 encoding 选项可用于指定用于解码 stdout 和 stderr 输出的字符编码。 如果 encoding 是 buffer、或一个无法识别的字符编码,则传入 Buffer 对象到回调函数。

child_process.spawn()

作用:使用给定的commandargs来衍生一个新进程。 如果省略 args,则默认为一个空数组。

child_process.spawn(command[, args][, options])

示例:

const {spawn} = require('child_process');
const childProcess = spawn('nginx', ['-s', 'reload']);
childProcess.stdout.on('data', (data) => {
	console.log(`stdout: ${data}`);
});
childProcess.stderr.on('data', (data) => {
	console.log(`stderr: ${data}`);
});
childProcess.on('close', (code) => {
	console.log(`子进程退出码:${code}`);
});

Windows平台一些问题

Windows上运行bat或者cmd文件

随着操作系统的不同,child_process.exec()child_process.execFile()会有一个重大区别,在类 Unix 操作系统上(Unix、 Linux、 macOS),child_process.execFile()效率更高,因为很多命令的执行不需要借助shell,但是在Windows上,.bat.cmd 文件在没有终端的情况下是不可执行的,因此不能使用 child_process.execFile() 启动。 当在 Windows 下运行时,要调用 .bat 和 .cmd 文件,必须主动传入cmd.exe

以上话是从官方文档摘抄的,但是不知为何我测试的结果不是这样的。

测试文件test.bat

@echo off
echo HelloWorld!
echo first params: %1
echo second params: %2

测试文件:

const {exec, execFile, spawn} = require('child_process');
const childProcess = spawn('test.bat', ['a1', 'b1']);
childProcess.stdout.on('data', (data) => {
	console.log(`stdout: ${data}`);
});
childProcess.stderr.on('data', (data) => {
	console.log(`stderr: ${data}`);
});
childProcess.on('close', (code) => {
	console.log(`子进程退出码:${code}`);
});
exec('test.bat a2 b2', (error, stdout, stderr) => {
	if(error) {
		console.error(`exec error: ${error}`);
		return;
	}
	console.log(`${stdout}`);
	console.log(`${stderr}`);
});
execFile('test.bat', ['a3', 'b3'], (error, stdout, stderr) => {
	if(error) {
		console.error(`exec error: ${error}`);
		return;
	}
	console.log(`${stdout}`);
	console.log(`${stderr}`);
});

输出结果:

可以发现,我没有使用cmd.exe也可以直接运行bat文件,所以有点郁闷,当然,像下面这样也是可以的:

const childProcess = spawn('cmd.exe', ['/c', 'test.bat', 'a1', 'b1']);
childProcess.stdout.on('data', (data) => {
	console.log(`stdout: ${data}`);
});
childProcess.stderr.on('data', (data) => {
	console.log(`stderr: ${data}`);
});

Windows下直接执行cmd命令

例如,新建一个名为test的文件夹:

exec('mkdir test', (error, stdout, stderr) => {
	if(error) {
		console.error(`exec error: ${error}`);
		return;
	}
	console.log(`${stdout}`);
	console.log(`${stderr}`);
});

不知为何,网上都说要借助一个node-cmd的模块,貌似不用借助第三方模块就可以直接调用Windows的命令(Linux的shell当然就更不在话下了)。

关于乱码问题

还是上面的nginx -s reload示例,当使用exec时,输出结果类似乱码(反正不正常),当使用spawn时一切正常,而且由于exec存在maxBuffer的问题,所以一般建议不怕麻烦的话优先使用spawn