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