並行して作成された他の3つのストリームから単一の出力ストリームを作成する


10

異なるフォーマットの3種類のデータがあります。データ型ごとに、それを単一の統一フォーマットに変換するPythonスクリプトがあります。

このPythonスクリプトは遅く、CPUにバインドされている(マルチコアマシンのシングルコアに)ため、スクリプトの3つのインスタンスを実行し(データタイプごとに1つ)、それらの出力を組み合わせてに渡しsortます。基本的に、これと同等です:

{ ./handle_1.py; ./handle_2.py; ./handle_3.py } | sort -n

しかし、3つのスクリプトが並行して実行されます。

ストリームを処理するスクリプトのn個のインスタンス間で、いくつかのstdoutストリームをラウンドロビンするためにGNU が使用されているこの質問を見つけましたsplit

分割されたmanページから:

-n, --number=CHUNKS
          generate CHUNKS output files.  See below
CHUNKS  may be:
 N       split into N files based on size of input
 K/N     output Kth of N to stdout
 l/N     split into N files without splitting lines
 l/K/N   output Kth of N to stdout without splitting lines
 r/N     like 'l'  but  use  round  robin  distributio

したがって、r/Nコマンドは「行を分割せずに意味します。

これに基づいて、次の解決策が実現可能であるようです:

split -n r/3 -u --filter="./choose_script" << EOF
> 1
> 2
> 3
> EOF

choose_scriptこれはどこにありますか:

#!/bin/bash
{ read x; ./handle_$x.py; }

残念ながら、私は行が混ざっているのを見ています-そしてそこにあるべきではない多くの改行があります。

たとえば、Pythonスクリプトを、これを実行するいくつかの単純なbashスクリプトに置き換えた場合:

#!/bin/bash
# ./handle_1.sh
while true; echo "1-$RANDOM"; done;

#!/bin/bash
# ./handle_2.sh
while true; echo "2-$RANDOM"; done;

#!/bin/bash
# ./handle_3.sh
while true; echo "3-$RANDOM"; done;

私はこの出力を見ます:

1-8394

2-11238
2-22757
1-723
2-6669
3-3690
2-892
2-312511-24152
2-9317
3-5981

これは煩わしいです-上で貼り付けたmanページの抜粋に基づいて、行の整合性を維持する必要があります。

明らかに、-u引数を削除すると機能しますが、バッファリングされ、1つを除いてすべてのスクリプトの出力をバッファリングするため、メモリが不足します。

誰かがここで何らかの洞察を持っている場合、それは非常に高く評価されるでしょう。私はここで私の深さを超えています。


#bashのfreenodeにいる一部の人々は、3つのプロセスすべてを生成してバックグラウンド化し、カスタムFDに書き込んでから、それらのFDをループしてそれらの行を読み取ることを提案しましたが、それを実行可能にする方法がわかりませんでした。また、coproc組み込みのbash を確認するように指示されましたが、どのように適用されるかはわかりません。
Cera

1
中間ファイルなしで実行する必要がありますか?あなただけではできませんjob1.py > file1 & job2.py > file 2 & job3.py > file3 ; wait ; sort -n file1 file2 file3か?
angus

回答:


2

GNU parallelの-uオプションを使用してみてください。

echo "1\n2\n3" | parallel -u -IX ./handle_X.sh

これは、プロセス全体をバッファリングせずに、それらを並行して実行します。


私は混乱して少しだ-あるXIX伝える-IことXが交換するためのフラグであるか、またはそれが適用され-X、一見、関連する意味を持つフラグが、?
Cera

ふん。私はこれを行っていparallel -u -X ./handle_{}.sh ::: "1" "2" "3"ます。
Cera

前者:を使用することもできますがparallel -u ./handle_{}.sh、中かっこにもコマンドを結合する意味があるため(質問のように)、変更することをお勧めします。
flowblok 2012年

:私はgrepは任意のマングリングをピックアップしていない、私のために動作するようですpastie.org/5113187(あなたは、テストのbashスクリプトを使用して、またはあなたの実際のPythonスクリプトされている?)
flowblok

問題は、それが実際には何も並行して実行していないことです。私はbashスクリプトを使用しています-pastie.org/5113225
Cera

2

試してください:

parallel ::: ./handle_1.py ./handle_2.py ./handle_3.py

handle_1.pyファイル名を取る場合:

parallel ::: ./handle_1.py ./handle_2.py ./handle_3.py ::: files*

出力を混合したくないので、-uを使用しないでください。

順序を維持したい場合(したがって、すべてのhandle_1出力がhandle_2の前にあるため、ソートを回避できる可能性があります):

parallel -k  ::: ./handle_1.py ./handle_2.py ./handle_3.py ::: files*

それでもソートしたい場合は、ソートを並列化して利用できますsort -m

parallel --files "./handle_{1}.py {2} | sort -n"  ::: 1 2 3 ::: files* | parallel -j1 -X sort -m

$ TMPDIRを、出力を保持するのに十分な大きさのディレクトリに設定します。


1
出力を「混合」したいのですが、最終出力のすべての行がサブプロセスの1つからの1行であることを確認したいだけです。混ぜない場合、システムは、まだ印刷されていないstdoutストリームをバッファリングするメモリを使い果たします。
Cera

GNU Parallelを使用すると、メモリ不足になることはありません。メモリにバッファリングされません。なぜメモリにバッファリングすると思いますか?
Ole Tange

2

多分私は何かが足りないのですが、あなたはただ行うことができません:

(./handle_1.py & ./handle_2.py & ./handle_3.py) | sort -n

各プロセスからの行がインターリーブされないようにする場合は、プロセス自体がそれらを完全に書き込みwrite、パイプへのsがアトミックであることが保証されている限り、出力バッファリングを無効にすることを確認する方が簡単です。PIPE_BUF。たとえば、あなたは確かにそれを作ることができないラà用出力バッファリングをstdioして、コールfflushまたは任意の同等の中にあるpython1つまたは少数のラインが書き込まれた後。

Pythonスクリプトを変更できない場合は、次のようにすることができます。

lb() { grep --line-buffered '^'; }

(GNU grepを使用)または:

lb() while IFS= read -r l; do printf '%s\n' "$l"; done

(コマンドの出力がテキストでない場合は、下のコメントの注記を参照してください)

そして、やります:

(./handle_1.py | lb & ./handle_2.py | lb & ./handle_3.py | lb) | sort -n

これらの3つのlbプロセスを回避する別のオプションは、select/ pollを使用する1つのコマンドに3つのパイプを使用して、出力がどこから来るかを確認し、それをsortラインベースにフィードすることですが、少しプログラミングが必要です。


あなたwaitはそこに必要だと思います。
derobert 2012年

1
一部のプログラムが終了する前にstdoutを閉じない限り、パイプsort -nは、fdが開かれているすべてのプログラムが終了するまで残ります。
ステファンChazelas

確かに、私はテストしました、あなたは正しいです。
derobert 2012年

いいえ、まだ出力が壊れています。ラインは混合され、インターリーブされます。
Cera

1
OK @Cerales、私の更新の答えを参照
ステファンChazelas

1

Flowbokの答えは正しい解決策でした。奇妙なことに、GNU parallelの出力は、それがファイルに直接出力される場合は破損しますが、ttyに送信する場合は破損しません。

幸い、script -cttyを模倣することができます。

まだ3つのスクリプトがあります。

#!/bin/bash
# handle_1.sh
while true; do echo "1-$RANDOM$RANDOM$RANDOM$RANDOM"; done

#!/bin/bash
# handle_2.sh
while true; do echo "2-$RANDOM$RANDOM$RANDOM$RANDOM"; done

#!/bin/bash
# handle_3.sh
while true; do echo "3-$RANDOM$RANDOM$RANDOM$RANDOM"; done

次に、並列への呼び出しをカプセル化するファイルがあります。

#!/bin/bash
# run_parallel.sh
parallel -u -I N ./handle_N.sh ::: "1" "2" "3"

そして、私はそれを次のように呼びます:

script -c ./run_parallel > output

出力の行は、さまざまなスクリプトの出力間で行ごとに混在していますが、特定の行で分解またはインターリーブされません。

奇妙な動作parallel-バグレポートを提出することがあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.