# print 出力をフィルタしてみる #perl {{tag: perl}} perl の `print` 関数の出力をフィルタしたい。 {{cpan: Benchmark::Confirm}} というモジュールを書いていたのですが、これは {{cpan: Benchmark}} のメソッドを一部上書いて欲しい機能を実装している。で、その出力をちょっとフィルタしたいという要件があり、とはいえ、元の {{cpan: Benchmark}} にある `print` の全てで渡す前に何かするというのはちょっと難しい。というかやってられない。 # print 関数をオーバライドしちゃえば やりたいことは、print される文字列の前に `# ` を挿入して valid TAP な出力にしたい感じでした。 ばっくりと STDOUT 全部そうなって良さそうだったので、最初は print 関数をオーバーライドしてしまえばいいと思ったけど、それはダメでした。 $ perl -le '*CORE::GLOBAL::print = sub { CORE::print "aki" }; print "foo";' foo 理由は `print` がサブルーチンプロトタイプで表現できない関数だから、という感じ。 $ perl -le 'my $which = (defined prototype("CORE::print")) ? "can override" : "impossible"; print $which;' impossible [[http://d.hatena.ne.jp/gfx/20091213/1260676300|gfxさんのブログに説明があります]]。 まあ、やりくちとしても強引過ぎる感じがしますね。 # STDOUT キャプチャすればよさげ いたって普通の結論ですが、今回はターゲットが STDOUT に print されるものだけだったので、STDOUT をキャプチャして、フィルタした結果を返せば実現できそう。 ふむふむ。`*STDOUT` をどっかむけて確保しといて、フィルタしつつあとでごにょごにょ出力、、{{cpan: Capture::Tiny}} 使ってテストでよくやるようなことをやればいいかなと思ったけど、今回は前述の通り、別モジュールのメソッドを上書いているのでちょっとやりにくい。コードブロックを囲むのが難しい。 というわけで CPAN を探してみると、その名もずばり {{cpan: IO::Capture}} の {{cpan: IO::Capture::Stdout}} がありました。 以下のような感じで start / stop の間の STDOUT をキャプチャしといて、あとで read してごにょごにょできる。 use IO::Capture::Stdout; my $capture = IO::Capture::Stdout->new; $capture->start; print STDOUT "Foo\n"; print STDOUT "Bar\n"; $capture->stop; while (my $line = $capture->read) { print "$line"; } かくして {{cpan: Benchmark::Confirm}} 0.04 の TAP 出力は実現できました。おわり。