概述
中文文档: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:可选的选项,常见有:
encoding
、maxBuffer
等,具体看文档; - callback:回调,有3个参数:error, stdout, stderr,会在子进程完全结束时才会触发;
exec
、execFile
和spawn
最大的区别是前面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()
execFile
和exec
很类似,二者的主要区别是:
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()
作用:使用给定的command
和args
来衍生一个新进程。 如果省略 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
。