パイプが「止まる」理由: バッファリング
- 問題の説明: ログファイルから特定の出力を見つけるために
tail -f /some/log/file | grep thing1 | grep thing2 コマンドを実行すると、ログ行がゆっくり追加される場合に出力が表示されないことがある。これはパイプが止まっているように見えるが、実際にはプログラムがデータをパイプに書き込んでいないため。
バッファリングの原因
- バッファリングする理由: プログラムがデータをパイプやファイルに書き込む前にバッファリングするのは一般的。これは性能向上のため、すべての出力を即座に書き込む代わりに、一定量のデータをまとめて一度に書き込むため。
- 例:
grep thing1 は 8KB のデータがたまるまでマッチしたデータを保持し、その結果、出力が現れないことがある。
ターミナルに書き込むときはバッファリングしない
- ターミナルとパイプの違い:
grep は出力先がターミナルのときは行バッファリングを使うが、パイプのときはブロックバッファリングを使う。これは isatty 関数によって判定される。
バッファリングするコマンドとしないコマンド
- バッファリングしないコマンド:
tail, cat, tee などはバッファリングしない。
- バッファリングするコマンド:
grep, sed, awk, tcpdump, jq, tr, cut などはバッファリングし、一部は特定のフラグでバッファリングを無効化できる。
プログラミング言語の標準出力バッファリング
- バッファリングする言語: C, Python, Ruby, Perl などは標準で出力をバッファリングし、特定の方法で無効化できる。
Ctrl-C を押したときのバッファ内容の損失
- 問題の説明:
Ctrl-C を押すとバッファ内の内容が失われる。これは SIGINT シグナルが先に送られるため。
- 解決策:
tcpdump の PID を見つけて kill -TERM $PID を実行すれば、バッファをフラッシュできる。
ファイルへリダイレクトするときもバッファリングされる
- ファイルへのリダイレクト: ファイルへリダイレクトするときもバッファリングは発生するが、
Ctrl-C によるバッファ損失の問題は起きない。
バッファリングを避けるさまざまな方法
- 解決策 1: すぐ終了するプログラムを実行する。
- 解決策 2:
grep の --line-buffered フラグを使う。
- 解決策 3:
awk を使う。
- 解決策 4:
stdbuf を使う。
- 解決策 5:
unbuffer を使う。
バッファリングを無効化する環境変数
- アイデア:
PYTHON_UNBUFFERED のような標準的な環境変数があるとよい、という意見。NO_BUFFER のような変数が提案されている。
省略された内容
- 省略されたトピック: 行バッファリングと完全な非バッファリングの違い、stderr と stdout のバッファリングの違い、OS の TTY ドライバによるバッファリングなど。
1件のコメント
Hacker Newsの意見
バッファリングされたアクセスは、一定のバイト数に達したり一定時間が経過したらフラッシュすべき。これはハードウェアインターフェースで似た問題を解決する一般的な方法
システム全体のCPUがアイドル状態になったら、すべてのバッファをフラッシュしたい
NIXシステムを20年以上扱ってきたが、バッファリングの問題はいつも忘れてしまう
Unixを35年以上使ってきたが、バッファリングの動作を完全には理解していなかった。この説明は有益だった
「非バッファリング」と「行バッファリング」を混同している
バッファが存在するのは、画面に出力を表示するよりもバッファへ書き込むほうが相対的に非常に遅いから
Ctrl-Cを押すと、バッファの内容が失われる可能性がある
Unixでバッファリングの問題に遭遇したことがあり、すべての
awk実装が同じように動作するわけではない凍りついたパイプのジョークを見逃した気分だ