child_process模塊用于新建子進程。子進程的運行結果儲存在系統緩存之中(最大200KB),等到子進程運行結束以后,主進程再用回調函數讀取子進程的運行結果。
[TOC]
## exec()
exec方法用于執行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方法用于新建一個子進程,然后緩存它的運行結果,運行結束后調用回調函數。
exec方法的第一個參數是所要執行的shell命令,第二個參數是回調函數,該函數接受三個參數,分別是發生的錯誤、標準輸出的顯示結果、標準錯誤的顯示結果。
由于標準輸出和標準錯誤都是流對象(stream),可以監聽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);
});
~~~
上面的代碼還表明,子進程本身有close事件,可以設置回調函數。
上面的代碼還有一個好處。監聽data事件以后,可以實時輸出結果,否則只有等到子進程結束,才會輸出結果。所以,如果子進程運行時間較長,或者是持續運行,第二種寫法更好。
下面是另一個例子,假定有一個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);
}
});
~~~
運行后,該文件的輸出結果如下。
~~~
$ node child.js
stdout: v0.11.14
stderr:
~~~
exec方法會直接調用bash(`/bin/sh`程序)來解釋命令,所以如果有用戶輸入的參數,exec方法是不安全的。
~~~
var path = ";user input";
child_process.exec('ls -l ' + path, function (err, data) {
console.log(data);
});
~~~
上面代碼表示,在bash環境下,`ls -l; user input`會直接運行。如果用戶輸入惡意代碼,將會帶來安全風險。因此,在有用戶輸入的情況下,最好不使用exec方法,而是使用execFile方法。
## execFile()
execFile方法直接執行特定的程序,參數作為數組傳入,不會被bash解釋,因此具有較高的安全性。
~~~
var child_process = require('child_process');
var path = ".";
child_process.execFile('/bin/ls', ['-l', path], function (err, result) {
console.log(result)
});
~~~
上面代碼中,假定path來自用戶輸入,如果其中包含了分號或反引號,ls程序不理解它們的含義,因此也就得不到運行結果,安全性就得到了提高。
## spawn()
spawn方法創建一個子進程來執行特定命令,用法與execFile方法類似,但是沒有回調函數,只能通過監聽事件,來獲取運行結果。它屬于異步執行,適用于子進程長時間運行的情況。
~~~
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方法接受兩個參數,第一個是可執行文件,第二個是參數數組。
spawn對象返回一個對象,代表子進程。該對象部署了EventEmitter接口,它的data事件可以監聽,從而得到子進程的輸出結果。
spawn方法與exec方法非常類似,只是使用格式略有區別。
~~~
child_process.exec(command, [options], callback)
child_process.spawn(command, [args], [options])
~~~
## fork()
fork方法直接創建一個子進程,執行Node腳本,`fork('./child.js')`?相當于?`spawn('node', ['./child.js'])`?。與spawn方法不同的是,fork會在父進程與子進程之間,建立一個通信管道,用于進程之間的通信。
~~~
var n = child_process.fork('./child.js');
n.on('message', function(m) {
console.log('PARENT got message:', m);
});
n.send({ hello: 'world' });
~~~
上面代碼中,fork方法返回一個代表進程間通信管道的對象,對該對象可以監聽message事件,用來獲取子進程返回的信息,也可以向子進程發送信息。
child.js腳本的內容如下。
~~~
process.on('message', function(m) {
console.log('CHILD got message:', m);
});
process.send({ foo: 'bar' });
~~~
上面代碼中,子進程監聽message事件,并向父進程發送信息。
## send()
使用 child_process.fork() 生成新進程之后,就可以用 child.send(message, [sendHandle]) 向新進程發送消息。新進程中通過監聽message事件,來獲取消息。
下面的例子是主進程的代碼。
~~~
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' });
~~~
下面是子進程sub.js代碼。
~~~
process.on('message', function(m) {
console.log('CHILD got message:', m);
});
process.send({ foo: 'bar' });
~~~
## 參考鏈接
* Lift Security Team,?[Avoiding Command Injection in Node.js](https://blog.liftsecurity.io/2014/08/19/Avoid-Command-Injection-Node.js): 為什么execFile()的安全性高于exec()
* Krasimir Tsonev,?[Node.js: managing child processes](http://tech.pro/tutorial/2074/nodejs-managing-child-processes)
* byvoid,?[Node.js中的child_process及進程通信](https://www.byvoid.com/zhs/blog/node-child-process-ipc): exec()、execFile()、fork()、spawn()四種方法的簡介
- 第一章 導論
- 1.1 前言
- 1.2 為什么學習JavaScript?
- 1.3 JavaScript的歷史
- 第二章 基本語法
- 2.1 語法概述
- 2.2 數值
- 2.3 字符串
- 2.4 對象
- 2.5 數組
- 2.6 函數
- 2.7 運算符
- 2.8 數據類型轉換
- 2.9 錯誤處理機制
- 2.10 JavaScript 編程風格
- 第三章 標準庫
- 3.1 Object對象
- 3.2 Array 對象
- 3.3 包裝對象和Boolean對象
- 3.4 Number對象
- 3.5 String對象
- 3.6 Math對象
- 3.7 Date對象
- 3.8 RegExp對象
- 3.9 JSON對象
- 3.10 ArrayBuffer:類型化數組
- 第四章 面向對象編程
- 4.1 概述
- 4.2 封裝
- 4.3 繼承
- 4.4 模塊化編程
- 第五章 DOM
- 5.1 Node節點
- 5.2 document節點
- 5.3 Element對象
- 5.4 Text節點和DocumentFragment節點
- 5.5 Event對象
- 5.6 CSS操作
- 5.7 Mutation Observer
- 第六章 瀏覽器對象
- 6.1 瀏覽器的JavaScript引擎
- 6.2 定時器
- 6.3 window對象
- 6.4 history對象
- 6.5 Ajax
- 6.6 同域限制和window.postMessage方法
- 6.7 Web Storage:瀏覽器端數據儲存機制
- 6.8 IndexedDB:瀏覽器端數據庫
- 6.9 Web Notifications API
- 6.10 Performance API
- 6.11 移動設備API
- 第七章 HTML網頁的API
- 7.1 HTML網頁元素
- 7.2 Canvas API
- 7.3 SVG 圖像
- 7.4 表單
- 7.5 文件和二進制數據的操作
- 7.6 Web Worker
- 7.7 SSE:服務器發送事件
- 7.8 Page Visibility API
- 7.9 Fullscreen API:全屏操作
- 7.10 Web Speech
- 7.11 requestAnimationFrame
- 7.12 WebSocket
- 7.13 WebRTC
- 7.14 Web Components
- 第八章 開發工具
- 8.1 console對象
- 8.2 PhantomJS
- 8.3 Bower:客戶端庫管理工具
- 8.4 Grunt:任務自動管理工具
- 8.5 Gulp:任務自動管理工具
- 8.6 Browserify:瀏覽器加載Node.js模塊
- 8.7 RequireJS和AMD規范
- 8.8 Source Map
- 8.9 JavaScript 程序測試
- 第九章 JavaScript高級語法
- 9.1 Promise對象
- 9.2 有限狀態機
- 9.3 MVC框架與Backbone.js
- 9.4 嚴格模式
- 9.5 ECMAScript 6 介紹
- 附錄
- 10.1 JavaScript API列表
- 草稿一:函數庫
- 11.1 Underscore.js
- 11.2 Modernizr
- 11.3 Datejs
- 11.4 D3.js
- 11.5 設計模式
- 11.6 排序算法
- 草稿二:jQuery
- 12.1 jQuery概述
- 12.2 jQuery工具方法
- 12.3 jQuery插件開發
- 12.4 jQuery.Deferred對象
- 12.5 如何做到 jQuery-free?
- 草稿三:Node.js
- 13.1 Node.js 概述
- 13.2 CommonJS規范
- 13.3 package.json文件
- 13.4 npm模塊管理器
- 13.5 fs 模塊
- 13.6 Path模塊
- 13.7 process對象
- 13.8 Buffer對象
- 13.9 Events模塊
- 13.10 stream接口
- 13.11 Child Process模塊
- 13.12 Http模塊
- 13.13 assert 模塊
- 13.14 Cluster模塊
- 13.15 os模塊
- 13.16 Net模塊和DNS模塊
- 13.17 Express框架
- 13.18 Koa 框架