[朝花夕拾]關於nodejs中child_process之中spawn和exec的區別
近日整理以前的node專案時發現一個檔案同時聲明瞭spawn和exec兩種方法。最後只是用了spawn方法,所以查了一下這兩者的區別。
Nodejs基於事件驅動來處理併發,本身是單執行緒模式執行的。Nodejs通過使用child_process模組來生成多個子程序來處理其他事物。主要包括4個非同步程序函式(spawn,exec,execFile,fork)和3個同步程序函式(spawnSync,execFileSync,execSync)。以非同步函式中spawn是最基本的建立子程序的函式,其他三個非同步函式都是對spawn不同程度的封裝。spawn只能執行指定的程式,引數需要在列表中給出,而exec可以直接運行復雜的命令。
比如要執行 du -sh /disk1 命令, 使用spawn函式需要寫成spawn(‘du‘, [‘-sh ‘, ‘/disk1’]),而使用exec函式時,可以直接寫成exec(‘du -sh /disk1’)。exec是會先進行Shell語法解析,因此用exec函式可以更方便的使用複雜的Shell命令,包括管道、重定向等。
一,var exec =require('child_process').exec;
exec在子程序輸出結果將放入buffer中,在結果返回完全之後,再將輸出一次性地以回撥函式引數的形式返回。如果exec的buffer體積設定的不夠大,它將會以一個“maxBuffer exceeded”錯誤失敗告終。另外,exec函式是對spawn的一種友好封裝,增加Shell命令解析,可以直接嵌入複雜的命令
a) 語法:child_process.exec(command[, options], callback)
b) 例項:
// 使用wget下載檔案的函式
vardownload_file_wget = function(file_url) {
// 提取檔名
var file_name =url.parse(file_url).pathname.split('/').pop();
// 組合wget命令
var wget = 'wget -P ' + DOWNLOAD_DIR + ' ' +file_url;
// 使用exec執行wget命令
var child = exec(wget, function(err, stdout,stderr) {
if (err) throw err;
else console.log(file_name + ' downloadedto ' + DOWNLOAD_DIR);
});
};
//執行cat統計檔案
varchildProcess = require('child_process');
var ls =childProcess.exec('cat *.js | wc', function (error, stdout, stderr) {
if (error) {
console.log(error.stack);
console.log('Error code: '+error.code);
}
console.log('Child Process STDOUT:'+stdout);
});
二,var spawn =require('child_process').spawn;
spawn在子執行緒開始執行後,就開始不斷將資料從子程序返回給主程序。從語法中我們可以發現與exec的一個區別是spawn是不支援callback函式的,它通過流的方式發資料傳給主程序,從而實現了多程序之間的資料交換。這個功能的直接用應用場景就是“系統監控”。在Linux下,我們有很多命令列工具,可以實時監控CPU,記憶體,IO,網路等資料,那麼用Node的child_process模組可以很容易地把這些資料採集的我們自己的應用中。
a) 語法:child_process.spawn(command[, args][, options])
b) 例項:
//這個例項就是所做的專案中nodejs實現堡壘機中生成子執行緒遠端登入的方法
Var worker = spawn('telnet', ['202.198.0.123']);
client.telnet = worker.stdin;
worker.on('close', function (code){
client.fileWriteStream.end();
//client.status = 2;
client.socket.write('\n\r');
client.socket.write('[Common]');
client.socket.write('[INFO] :Connection to Device has closed!\n\r');
closeSocket(client.socket,client);
client.socket.destroy();
});
worker.stdout.on('data', function(data) {
data = data.toString();
if(client.status == 3){
return;
}
if((data.indexOf('nmslogin')>=0)&&(client.isLogin==0)){
client.secret = 1;
client.telnet.write('username\n');
}elseif((data.indexOf('Password')>=0)&&(client.isLogin==0)){
client.telnet.write('password\n');
client.isLogin = 1;
}elseif(data.indexOf('Password')>=0&& client.status == 4){
client.notChange= 1;
client.fileWriteStream.write(data);
client.socket.write(data);
client.datas =client.datas+data.toString();
}else {
client.fileWriteStream.write(data);
client.socket.write(data);
client.datas =client.datas+data.toString();
}
});
worker.on('error',function(error){
client.fileWriteStream.end();
client.status = 2;
client.socket.write('[ERR2] :Anerror occured!')
client.socket.write('[INFO] :Connection to Device has closed!\n\r');
//client.socket.write('>>>>Input Device IP:');
closeSocket(client.socket,client);
});
擴充套件閱讀:
JavaScript 執行機制詳解:再談Event Loop
Node.js軟肋之回撥大坑
Node.js的執行緒和程序
Linux下同步模式、非同步模式、阻塞呼叫、非阻塞呼叫總結