libc の buffer と perl の buffer

libc の buffer と perl の buffer は違うんだよという話を聞いたのでちょい調べてみた。

libc の buffer は grep を pipe で複数つなぐとなかなか表示されないやつ

$ while : ; do echo hoge; sleep 1; done | grep hoge

↑これはすぐに hoge が表示されますが

$ while : ; do echo hoge; sleep 1; done | grep hoge | grep hoge

↑こっちはずーっと待ってないと出力されません。出力先が端末でない場合、fwrite とかは buffering されるんですね。すぐに出力したい場合は次のように

$ while : ; do echo hoge; sleep 1; done | grep --line-buffered hoge | grep hoge

“–line-buffered” オプションをつけることで、毎行 fflush() が実行されてすぐさま出力されます。もうひとつ

$ while : ; do echo hoge; sleep 1; done | stdbuf -o0 grep hoge | grep hoge

と、stdbuf で LD_PRELOAD を使って buffer をコントロールするという方法もあるようです。(How to fix stdio buffering)

で、Perl もこんな仕組みで buffering されてるんだろうと思ってたら違うんですね。
7.12. Flushing Output

$ perl -e 'while (1) { print "hoge\n"; sleep 1; }' | grep hoge
$ stdbuf -o0 perl -e 'while (1) { print "hoge\n"; sleep 1; }' | grep hoge

このどちらもすぐには出力されません。
次のようにするしかないようです。(IO::Handle 使っても良い)

$ perl -e '$|=1; while (1) { print "hoge\n"; sleep 1; }' | grep hoge

なるほどねぇ。

awk には fflush() っていう関数があって、sed には –unbuffered というオプションがあるらしい。

grep, awk, sed でバッファしない方法

コメント