stdbufコマンドはI/Oストリームのバッファリングを修正してコマンドを実行することができます。
'tail -f'等のプログラムが終了せずに内容を表示し続けるコマンドをパイプを通してフィルタして、さらにパイプを通すとき内容がバッファリングされてなかなか端末に表示がされない場合があります。
そのようなときにstdbufコマンドでバッファリングを調整することで、すぐに端末に表示できるようにできます。
目次
I/Oストリームのバッファリングについて
バッファリングには3つのタイプがあります。
- バッファをしない(アンバッファ, unbuffered)
- ブロックバッファ(block buffered)
- ラインバッファ(line buffered)
出力のストリームがバッファをしない場合は、ファイルや端末に内容がすぐに書き込まれます。
ブロックバッファの場合は、ある程度の内容が蓄えてから、ファイルや端末に内容が書き込まれます。
ラインバッファの場合は、改行コードまでの内容を蓄えてから、または標準入力から入力を読み込むまで、ファイルや端末に内容が書き込まれます。
基本的に、全てのファイルはブロックバッファを利用して、バッファリングが行われます。
ただし、(通常の標準出力のような)端末を参照したストリームの場合はラインバッファになります。
また、stderrはデフォルトではバッファをしません。
stdbufコマンドの構文とオプション
stdbufコマンドの構文
stdbuf option… command
stdbufコマンドのオプション
オプション | 意味 |
-i mode --input=mode |
標準入力のバッファを調整 |
-o mode --output=mode |
標準出力のバッファを調整 |
-e mode --error=mode |
標準エラー出力のバッファを調整 |
modeの指定方法
mode | 意味 |
L | ラインバッファ -iオプションでは無視 |
0 | ゼロを指定でバッファをしない(アンバッファ) |
size(数字) | ブロックバッファ 数字を指定(例: 4096, 8K, 1M 等) |
stdbufコマンドの利用例
出力ストリームを調整
(-oオプション)
-oオプションを指定して、stdbufコマンドを利用すると出力ストリームのバッファリングを調整することができます。
これは、'tail -f'等でログを表示し、パイプを通してcutやuniq等のフィルタコマンドを利用する場合に使用できます。
例えば、下のコマンド例はstdbufコマンドのmanページの例にあるコマンドになります。これはアクセス解析などの結果がすぐに表示される例になります。
コマンド例
1 |
tail -f access.log | stdbuf -oL cut -d ' ' -f1 | uniq |
tail -fのコマンドはバッファをすぐに書き出します。このコマンドでstdbufコマンドを利用している理由は、cutコマンドの出力をパイプに通すとき、元々のパイプへの出力がブロックバッファですぐにcutコマンドの出力がuniqコマンドに渡されず、ボトルネックのような状態だったためです。
これをラインバッファに切り替えるとcutコマンドの出力が一行ごとにuniqコマンドへ渡されるようになります。そのあとのuniqコマンドへの出力は、端末でラインバッファになるので、上のコマンド例は結果がすぐに表示されるようになります。
入力ストリームを調整
(-iオプション)
入力ストリームの調整は、コマンドへの入力をどのくらいとるかを調整することができます。例えば、sedコマンドを複合コマンドとして利用する場合に分かりやすいです。
まず、以下のコマンドの出力は3行になります。
1 2 3 4 |
$printf 'aaa\nbbb\nccc\n' aaa bbb ccc |
これに以下の複合コマンドを利用すると出力が一行になります。
1 2 |
$printf 'aaa\nbbb\nccc\n' | ( sed 1q ; sed 1q ; sed 1q ) aaa |
これは、最初のsedコマンドで全ての内容が入力のブロックバッファとして利用され、そのあとのsedコマンドに対して、入力が残っていないためになります。
最初のsedコマンドに対してstdbufコマンドを利用すると、入力のバッファリングをしないように調整すると2行目が表示されます。
1 2 3 |
$printf 'aaa\nbbb\nccc\n' | ( stdbuf -i0 sed 1q ; sed 1q ; sed 1q ) aaa bbb |
同様に2つ目のsedコマンドに対して、入力のバッファリングを調整すると3行目が表示されます。
1 2 3 4 |
$printf 'aaa\nbbb\nccc\n' | ( stdbuf -i0 sed 1q ; stdbuf -i0 sed 1q ; sed 1q ) aaa bbb ccc |
コマンド例と実行結果
1 2 3 4 |
$printf 'aaa\nbbb\nccc\n' | ( sed -u 1q ; sed -u 1q ; sed -u 1q ) aaa bbb ccc |
例えば、8バイトを繰り返し入力するためにyesコマンドを使用し、何回繰り返すかをheadコマンドを用いてみます。
最初は4096バイト(8×512)を出力すると
1 2 |
$yes 'abcdefg' | head -n 512 | ( sed 1q; sed 1q; sed 1q ) abcdefg |
1行が表示されます。また、yesコマンドで繰り返している8バイトは改行コードを含めて、8バイトになるため、7文字で設定されています。
次に4096+8バイトを出力すると
1 2 3 |
$yes 'abcdefg' | head -n 513 | ( sed 1q; sed 1q; sed 1q ) abcdefg abcdefg |
2行目が表示されます。同様に8192バイトを出力すると
1 2 3 |
$yes 'abcdefg' | head -n 1024 | ( sed 1q; sed 1q; sed 1q ) abcdefg abcdefg<br> |
出力は2行のままで、8192+8バイトにすると
1 2 3 4 |
$yes 'abcdefg' | head -n 1025 | ( sed 1q; sed 1q; sed 1q ) abcdefg abcdefg abcdefg |
3行目が表示されます。
Linuxコマンドでバッファリングについて意識することはあまりないかもしれませんが、覚えておくとよいかもしれません。