Node.js Child Process模塊

2021-09-15 16:32 更新

child_process模塊用于新建子進(jìn)程。子進(jìn)程的運(yùn)行結(jié)果儲(chǔ)存在系統(tǒng)緩存之中(最大200KB),等到子進(jìn)程運(yùn)行結(jié)束以后,主進(jìn)程再用回調(diào)函數(shù)讀取子進(jìn)程的運(yùn)行結(jié)果。

exec()

exec方法用于執(zhí)行bash命令。

var exec = require('child_process').exec;

var ls = exec('ls -l', function (error, stdout, stderr) {
   if (error) {
     console.log(error.stack);
     console.log('Error code: '+error.code);
   }
   console.log('Child Process STDOUT: '+stdout);
});

上面代碼的exec方法用于新建一個(gè)子進(jìn)程,然后緩存它的運(yùn)行結(jié)果,運(yùn)行結(jié)束后調(diào)用回調(diào)函數(shù)。

exec方法的第一個(gè)參數(shù)是所要執(zhí)行的shell命令,第二個(gè)參數(shù)是回調(diào)函數(shù),該函數(shù)接受三個(gè)參數(shù),分別是發(fā)生的錯(cuò)誤、標(biāo)準(zhǔn)輸出的顯示結(jié)果、標(biāo)準(zhǔn)錯(cuò)誤的顯示結(jié)果。

由于標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤都是流對(duì)象(stream),可以監(jiān)聽(tīng)data事件,因此上面的代碼也可以寫成下面這樣。

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);
});

上面的代碼還表明,子進(jìn)程本身有close事件,可以設(shè)置回調(diào)函數(shù)。

上面的代碼還有一個(gè)好處。監(jiān)聽(tīng)data事件以后,可以實(shí)時(shí)輸出結(jié)果,否則只有等到子進(jìn)程結(jié)束,才會(huì)輸出結(jié)果。所以,如果子進(jìn)程運(yùn)行時(shí)間較長(zhǎng),或者是持續(xù)運(yùn)行,第二種寫法更好。

下面是另一個(gè)例子,假定有一個(gè)child.js文件。

// child.js

var exec = require('child_process').exec;
exec('node -v', function(error, stdout, stderr) {
    console.log('stdout: ' + stdout);
    console.log('stderr: ' + stderr);
    if (error !== null) {
        console.log('exec error: ' + error);
    }
});

運(yùn)行后,該文件的輸出結(jié)果如下。

$ node child.js

stdout: v0.11.14

stderr:

exec方法會(huì)直接調(diào)用bash(/bin/sh程序)來(lái)解釋命令,所以如果有用戶輸入的參數(shù),exec方法是不安全的。

var path = ";user input";
child_process.exec('ls -l ' + path, function (err, data) {
  console.log(data);
});

上面代碼表示,在bash環(huán)境下,ls -l; user input會(huì)直接運(yùn)行。如果用戶輸入惡意代碼,將會(huì)帶來(lái)安全風(fēng)險(xiǎn)。因此,在有用戶輸入的情況下,最好不使用exec方法,而是使用execFile方法。

execFile()

execFile方法直接執(zhí)行特定的程序,參數(shù)作為數(shù)組傳入,不會(huì)被bash解釋,因此具有較高的安全性。

var child_process = require('child_process');

var path = ".";
child_process.execFile('/bin/ls', ['-l', path], function (err, result) {
    console.log(result)
});

上面代碼中,假定path來(lái)自用戶輸入,如果其中包含了分號(hào)或反引號(hào),ls程序不理解它們的含義,因此也就得不到運(yùn)行結(jié)果,安全性就得到了提高。

spawn()

spawn方法創(chuàng)建一個(gè)子進(jìn)程來(lái)執(zhí)行特定命令,用法與execFile方法類似,但是沒(méi)有回調(diào)函數(shù),只能通過(guò)監(jiān)聽(tīng)事件,來(lái)獲取運(yùn)行結(jié)果。它屬于異步執(zhí)行,適用于子進(jìn)程長(zhǎng)時(shí)間運(yùn)行的情況。

var child_process = require('child_process');

var path = '.';
var ls = child_process.spawn('/bin/ls', ['-l', path]);
ls.stdout.on('data', function (data) {
  console.log('stdout: ' + data);
});

ls.stderr.on('data', function (data) {
  console.log('stderr: ' + data);
});

ls.on('close', function (code) {
  console.log('child process exited with code ' + code);
});

spawn方法接受兩個(gè)參數(shù),第一個(gè)是可執(zhí)行文件,第二個(gè)是參數(shù)數(shù)組。

spawn對(duì)象返回一個(gè)對(duì)象,代表子進(jìn)程。該對(duì)象部署了EventEmitter接口,它的data事件可以監(jiān)聽(tīng),從而得到子進(jìn)程的輸出結(jié)果。

spawn方法與exec方法非常類似,只是使用格式略有區(qū)別。

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

fork()

fork方法直接創(chuàng)建一個(gè)子進(jìn)程,執(zhí)行Node腳本,fork('./child.js') 相當(dāng)于 spawn('node', ['./child.js']) 。與spawn方法不同的是,fork會(huì)在父進(jìn)程與子進(jìn)程之間,建立一個(gè)通信管道,用于進(jìn)程之間的通信。

var n = child_process.fork('./child.js');
n.on('message', function(m) {
  console.log('PARENT got message:', m);
});
n.send({ hello: 'world' });

上面代碼中,fork方法返回一個(gè)代表進(jìn)程間通信管道的對(duì)象,對(duì)該對(duì)象可以監(jiān)聽(tīng)message事件,用來(lái)獲取子進(jìn)程返回的信息,也可以向子進(jìn)程發(fā)送信息。

child.js腳本的內(nèi)容如下。

process.on('message', function(m) {
  console.log('CHILD got message:', m);
});
process.send({ foo: 'bar' });

上面代碼中,子進(jìn)程監(jiān)聽(tīng)message事件,并向父進(jìn)程發(fā)送信息。

send()

使用 child_process.fork() 生成新進(jìn)程之后,就可以用 child.send(message, [sendHandle]) 向新進(jìn)程發(fā)送消息。新進(jìn)程中通過(guò)監(jiān)聽(tīng)message事件,來(lái)獲取消息。

下面的例子是主進(jìn)程的代碼。

var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
  console.log('PARENT got message:', m);
});

n.send({ hello: 'world' });

下面是子進(jìn)程sub.js代碼。

process.on('message', function(m) {
  console.log('CHILD got message:', m);
});

process.send({ foo: 'bar' });

參考鏈接

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)