Linuxの「リーク」パイプ

Linuxの「リーク」パイプ

次のパイプラインがあるとします。

$ a | b

stdinの処理を中止すると、bしばらくするとパイプがいっぱいになり、stdoutが書き込みをaブロックします(b処理を再開または終了するまで)。

これを避けたい場合は、buffer(1)次のように大きなパイプ(またはより簡単に)を使用したい場合があります。

$ a | buffer | b

これは私にもっと時間をかけますが、最終的には停止しaます。

私が望むのは、(私が解決している非常に具体的なシナリオについて)パイプがいっぱいになると、バッファから一部のデータを削除して(理想的には1行ずつ)処理を続行できるaようにする「リーク」パイプを持つことです。パイプラインを流れるデータは消費されます。つまり、データ処理はブロックせずに実行できるbよりも重要ではありませんa

一般的に、私は制限的で漏れのあるバッファのようなものが欲しいです。

$ a | leakybuffer | b

おそらくどの言語でも簡単に実装できます。私が見逃した「すぐに使える」もの(またはbash one-linerのようなもの)があるかどうか疑問に思います。

注:この例では通常のパイプを使用していますが、質問は名前付きパイプにも適用されます。


以下に回答しましたが、以下の簡単な解決策にはいくつかの制限があるため、Leakybufferコマンドも実装することにしました。https://github.com/CAFxX/leakybuffer

ベストアンサー1

最も簡単な方法は、非ブロック出力を設定するいくつかのプログラムを介してパイプすることです。これは単純なPerl onelinerです(別の名前で保存できます)。リークバッファ)この方法:

だからあなたはa | b次のようになります:

a | perl -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { print }' | b

これが行うことは、入力を読み込み、出力を書き込むことです(と同じcat(1))。しかし、出力は非ブロックです。つまり、書き込みが失敗した場合はエラーを返し、データは失われますが、プロセスは次の入力行から続きます。私たちは便利にエラーを無視します。予想通り、プロセスはラインバッファですが、以下の警告を参照してください。

次のコマンドを使用してテストできます。

seq 1 500000 | perl -w -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { print }' | \
    while read a; do echo $a; done > output

次のように行が不足しているファイルが得られますoutput(正確な出力はシェル速度などによって異なります)。

12768
12769
12770
12771
12772
12773
127775610
75611
75612
75613

シェルが次に行を失う場所を見ることができます12773。しかし、それも例外です - Perlはバッファが十分ではない12774\nので1277書くだけです - それで、次の数字は75610行の先頭から始まりません。かなり醜いです。

これは、書き込みが完全に成功しなかった場合にPerlがそれを検出し、新しい着信行を無視しながら残りの行をフラッシュすることによって改善することができます。しかし、これはPerlスクリプトがより複雑になるため、興味のある読者のための練習として残ります。 )

アップデート(バイナリ用): 改行で終わる行(ログファイルなど)を処理しない場合は、コマンドを少し変更する必要があります。それ以外の場合、Perlは多くのメモリを消費します(入力に改行が表示される頻度によって異なります)。

perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (read STDIN, $_, 4096) { print }' 

また、バイナリファイルでも機能します(追加のメモリが消費されません)。

アップデート2 - より良いテキストファイル出力: 出力バッファを避けてください(syswrite代わりにprint):

seq 1 500000 | perl -w -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { syswrite STDOUT,$_ }' | \
    while read a; do echo $a; done > output

私に「マージライン」の問題を解決するようです。

12766
12767
12768
16384
16385
16386

(注:perl -ne '$c++; next if $c==$_; print "$c $_"; $c=$_' outputonelinerを使用して切り捨てられた出力行を確認できます。)

おすすめ記事