管道
通過“child_process”模塊fork出來的子進程都是返回一個ChildProcess對象實例,ChildProcess類比較特殊無法手動創建該對象實例,只能使用fork或者spawn,而且與process對象不同的是,ChildProcess實例的stdin為可寫流,stdout和stderr為可讀流。因此通過childprocess.stdin可以輸入數據,通過childprocess.stdout可將子進程的數據數據輸出到父進程中。
具體實現
var child = require('child_process')
, fs = require('fs');
var childProcess = child.exec(cmd, {env: process.env, maxBuffer: 20*1024*1024}, function(err) {
});
var stdoutStream = fs.createWriteStream(escape(stdoutFilePath));
childProcess.stdout.pipe(stdoutStream, {end: false});
childProcess.stderr.pipe(stdoutStream, {end: false});
// 使用node的后壓機制
childProcess.stdout.pipe(process.stdout);
childProcess.stderr.pipe(process.stderr);
var stdoutEnded = false, stderrEnded = false;
function tryClosing(){ if(stdoutEnded && stderrEnded){ stdoutStream.end(); } }
childProcess.stdout.on('end', function(){ stdoutEnded = true; tryClosing(); });
childProcess.stderr.on('end', function(){ stderrEnded = true; tryClosing(); });
這種方式適用於大多數場景,直接使用流特性完成子進程數據的輸出。
文件檢測
在某些系統的node環境下,“child_process”並未提供execSync特性,因此需要hack,這里參考shelljs的實現機制。
使用系統兼容較好的exec函數完成基本功能,在shell命令執行完畢后寫入狀態信息到某些臨時文件,最后通過循環不斷讀取新寫入該臨時文件的數據。由於在shell命令執行過程中需要模擬同步效果,因此在循環中不僅僅獲取新寫入的數據,同時需要模擬I/O阻塞操作,此處shelljs的作者通過嘗試所有的同步IO API,發現fs.writeFileSync操作可以較少的減輕CPU利用率,因此使用該函數阻塞I/O。
具體實現
var child = require('child_process')
, fs = require('fs');
var childProcess = child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: 20*1024*1024}, function(err) {
fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0');
});
var previousStdoutContent = '';
// Echoes stdout changes from running process, if not silent
function updateStdout() {
if (options.silent || !fs.existsSync(stdoutFile))
return;
var stdoutContent = fs.readFileSync(stdoutFile, 'utf8');
// No changes since last time?
if (stdoutContent.length <= previousStdoutContent.length)
return;
process.stdout.write(stdoutContent.substr(previousStdoutContent.length));
previousStdoutContent = stdoutContent;
}
