生活 + 筆記

1.20.2014

用 node.js 跑 shell script



怎麼用 node.js 跑 shell script 咧 ?

node child_process module 主要提供了四個 create child process 的 function 
1. exec
2. spawn
3. fork
4. execFile

這邊我就只介紹 exec & spawn 
然後一點點 fork …

spawn 是最原始的 create child process function,其他三個都是 spawn 的封裝
spawn 和 exec 的最大差別是
1. 帶入的參數不同 ( exec 比較靈活 ) 
2. 和 parent process 之間 io 處理上不同 ( 請參考 http://villadora-blog.herokuapp.com/2013/05/07/differences-between-exec-and-spawn/ 這邊就不多提 ~ ) 

直接來看程式碼

var cp = require('child_process'),
//spawn = cp.spawn,
exec = cp.exec,
//ls = spawn('ls', ['-lh', '/usr']);
ls = exec('ls -lh /usr');
 
ls.stdout.on('data', function(data) {
console.log('stdout: ' + data);
});
 
ls.on('close', function(code) {
console.log('child process exited with code' + code);
});

fork 的話是可以直接執行 node.js 的程式
像是 fork('fork_child.js'); 
而他和 spawn 的差別是 fork 可以會在 parent 和 child process 間建立一個 IPC ( Inter-process communication ) 用來做溝通

直接來看程式碼
fork_parent.js

var cp = require('child_process'),
     child = cp.fork('fork_child.js');
     // spawn('node', ['fork_child.js']);

child.on('message', function(message) {
     console.log('parent got a message from child: ', message);
});

child.send('Call me Daddy');

fork_child.js

var cp = require('child_process');

process.on('message', function(message) {
     console.log('child got a message from parent: ', message);
});

process.send('Daddy ~~~~~~~~~~');

然後這次我在 project 中會用到 node.js 的 child process module 是因為我要執行 shell 去掃所有的字串

shell command 長這樣 : 
execStr = 'grep -R --exclude-dir={lang,yrb,node_modules,compilers,sprites,uitests,tools,assets,tests} "' + str + '" ' + path + ' >> str.txt'; 

其實就是讀一個 yrb.json 檔然後掃 string 去 grep 然後輸出到某個 file 

現在知道可用 child_process module 來寫 shell script 

簡單啦 ~ 

我就寫一個 for 迴圈把 yrb.json 的每一個 string 讀出來再去做 grep 

但是 !!

遇到了一個問題 …

出現了這樣的 error 

node.js:201  throw e; // process.nextTick error, or error event on first tick               
^ Error: spawn EMFILE     
at errnoException (child_process.js:481:11)    
at ChildProcess.spawn (child_process.js:444:11)
at child_process.js:342:9   
at Object.execFile (child_process.js:252:15)
at child_process.js:220:18

我就用 google 大神找解法 
先是找到了 

1. 設定 ulimit -n 10000 ( 什麼是 ulimit http://homeserver.com.tw/2011/04/02/ulimit/  ) 

但不行 !!! 在我的 dev 最多就只能調到 1000 … ( 可能是硬體的限制吧 … ) 

然後就找到了另一個解法 

2. queue 

nodejs 是 non-blocking … 所以其實會去同時執行多個 process 這樣就會把資源給吃光光 … 
所以只好寫一個似 blocking 的方法來解決

var queue = [],
    MAX = 20,         // only allow 20 simultaneous exec calls     
    count = 0,        // holds how many execs are running     
    keywords = […];   // long list of urls  // our callback for each exec call 

function callback(err, stdout, stderr) {  
    count -= 1;     
    if (queue.length > 0 && count < MAX) {  // get next item in the queue!                count += 1;    
         var url = queue.shift();
         exec('shell command', wget_callback);  
    } 
}  

keywords.forEach( function(url) {  
    if (count < MAX) {  
    // go get the file!    
         count += 1;    
         exec('shell command', wget_callback);  
    } else {  
    // queue it up…    
        queue.push(url);  
    } 
});

這樣就可以解決一次跑太多 process 把資源吃光光的問題了 ! 

資料參考:
https://www.byvoid.com/blog/node-child-process-ipc/
http://homeserver.com.tw/2011/04/02/ulimit/
http://www.runtime-era.com/2012/10/quick-and-dirty-nodejs-exec-limit-queue.html
 

沒有留言:

張貼留言