exec與spawn方法的區別與陷阱

原由

前幾天以前寫的一段程序忽然報了個詭異的異常"maxBuffer exceeded",追進去發現是在一個上傳的模塊中解壓縮的時候調用了child_process.exec方法,在解壓某個上傳文件的時候拋異常了。而解壓其餘的文件就沒有問題。因而把這個文件找出來,單獨寫了個如出一轍的exec,調個子進程解壓縮一遍,發現也沒有錯誤,問題是一旦放到以前那段程序裏面就報錯了。因而只能去查API了。node

exec與spawn

在nodejs的child_process模塊中,有兩個相似的方法spawn和exec,都是經過生成一個子進程,去執行指定的命令,不過他們的用法稍有不一樣,在命令的指定上,exec相對靈活,等於一個shell的命令行,如'ps -ef | grep node'此類的管道操做也能一次性實現。
nodejs文檔用法e.x:shell

  var cp = require('child_process');
  //spawn
  var ls = cp.spawn('ls'/*command*/, ['-lh', '/usr']/*args*/, {}/*options, [optional]*/);
  ls.stdout.on('data', function (data) {
    console.log('stdout: ' + data);
  });

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

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

  //exec
  cp.exec('ls -lh /usr'/*command*/,{}/*options, [optiona]l*/, function(err, stdout, stderr){
    console.log('stdout: ' + stdout);
    console.log('stderr: ' + stderr);
  })

這時忽然發現了一個一直被我忽略了的參數options,用來設定子進程的環境和執行條件。
spawn的options默認爲:函數

  { 
    cwd: undefined,
    env: process.env,
    setsid: false
  }

exec的options默認爲:ui

  { 
    encoding: 'utf8',
    timeout: 0, /*子進程最長執行時間 */
    maxBuffer: 200*1024,  /*stdout和stderr的最大長度*/
    killSignal: 'SIGTERM',
    cwd: null,
    env: null
  }

問題解決

在exec的options中有一個選項maxBuffer,看到這就清楚了,估計是超出了這個maxBuffer。果真,回到剛纔單獨寫的解壓程序裏面,把maxBuffer設爲一個較小的值,就出現這個錯誤了。exec的默認stdout最大大小爲200K,剛纔寫的這個程序由於PWD跟以前的代碼不一樣,所以stdout變小了,致使問題沒有重現。把子進程中解壓文件的命令改爲靜默模式,就能夠暫時把這個問題解決了。固然,也能夠將其改寫成spawn來完成,就不會有maxBuffer的限制了。spa

本質

雖然在上面的文檔用法中,spwan和exec的最終回調方式有區別,可是在node的實現中,其實二者的實現方式是一致的,exec也能夠像spawn同樣使用,只不過exec在觸發stderr和stdout的data事件的時候,會把數據寫到字符串中,到執行結束或者錯誤退出的時候經過回調函數傳遞出來,實現了exec這種便捷用法。pwa

  var cp = require('child_process');
  //exec能夠像spawn同樣使用
  var ls = cp.exec('ls -lh /usr', {}/*options, [optional]*/);

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

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

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

使用注意

能夠看出,exec在使用的便捷性上要超過spawn,且執行速度上也相差無幾。不過這種便攜性是要付出必定代價的。在exec的options中,有一項是maxBuffer,若是執行的command輸出超出了這個長度,無論是採用回調函數方式,仍是emit data事件方式傳遞結果,都會拋出maxBuffer exceeded異常,而且殺死子進程。此時子進程可能已經執行完成(maxBuffer和須要長度相差不大,在收到最後一個數據包的時候才超出),也多是隻執行了一半。所以若是須要使用exec,就要慎重設置maxBuffer(和timeout),或者對執行的命令採用靜默方式(同時能夠略微提高執行速度)。
ps:感受nodejs能夠提供一個方法,把exec的回調函數傳遞結果(maxBuffer限制)去掉,保留exec的參數傳遞方式,就方便我這種小白了..命令行

相關文章
相關標籤/搜索