🔙 Back to Top

Fri Nov 27 21:22:21 JST 2015

node.js スクリプトを他のシェルスクリプトと組み合わせるとエラーを出すことがある

よく調べると、パイプにつないだ時にエラーが出ることがわかった. 再現する簡単なコードは次

   cat test.js
console.log(0);
console.log(1);

   node test.js
0
1

   node test.js | head -1
0
events.js:141
      throw er; // Unhandled 'error' event
            ^
Error: write EPIPE
    at Object.exports._errnoException (util.js:749:11)
    at exports._exceptionWithHostPort (util.js:772:20)
    at WriteWrap.afterWrite (net.js:753:14)

要するに、パイプの次のプロセスがコレ以上の入力を受け付けない為に、 書き出す先が閉じられているのが原因らしい. でも普通、だからってエラーは出さないのでは?

   ruby test.rb # ソースコード略
1
2

   ruby test.rb | head -1
1

この挙動が普通のはずだ. 調べてみて、コレ以上の原因はつかめなかったが、 mhart/epipebomb なるものを見つけた. 全く同じこの問題を、解決するためだけのモジュールらしい.

中身を見ると、なんということもなく、 process.stdout に error が投げられたらプロセスを終了するだけのものだ.

従って、次のように頭に一行追加することでエラーが防げる (握りつぶせる). UNIX的コマンドを書くときは、使おう.

   cat test.js
process.stdout.on('error',process.exit);
console.log(0);
console.log(1);

   node test.js | head -1
0

Tue Jan 5 12:35:59 JST 2016

Ruby でも同じ現象が起きた. 上の例のように出力が少ないとエラーは起きないが、ずっと大きくなるとやはり Broken pipe なるエラーが出る. バッファサイズの問題だろう.

   ruby -e "9.times{|i|p i}" | head -1
0

   ruby -e "10.times{|i|p i}" | head -1
0
-e:1:in `p': Broken pipe (Errno::EPIPE)
        from -e:1:in `block in <main>'
        from -e:1:in `times'
        from -e:1:in `<main>'

http://stackoverflow.com/questions/1807355/broken-pipe-errnoepipe にあるように例外処理すれば良い. Ruby の try 文の書き方を初めて目にした.

   ruby -e "begin;100.times{|i|p i};rescue Errno::EPIPE; end" | head -1
0