並列シェルループ


11

私は多くのファイルを処理したいのですが、ここにたくさんのコアがあるので、並行してやりたいです:

for i in *.myfiles; do do_something $i `derived_params $i` other_params; done

Makefile ソリューションは知っていますが、コマンドにはシェルグロビングリストにない引数が必要です。私が見つけたのは:

> function pwait() {
>     while [ $(jobs -p | wc -l) -ge $1 ]; do
>         sleep 1
>     done
> }
>

これを使用するには、ジョブとpwait呼び出しの後に実行するだけで、パラメーターは並列プロセスの数を示します。

> for i in *; do
>     do_something $i &
>     pwait 10
> done

しかし、これはあまりうまくいきません。たとえば、forループを使用して多くのファイルを変換しましたが、エラーが発生し、ジョブを元に戻せませんでした。

zshメーリングリストに関する議論は今ではとても古いので、私はこれがまだ行われていないと信じられません。それで、あなたはもっと良く知っていますか?


この質問に似ています:superuser.com/questions/153630/…そのテクニックがうまくいくかどうかを確認してください。
-JRobert

エラーメッセージを投稿しておくと役立ちます。
追って通知があるまで一時停止します。

@JRobertはい、私はこれを知っていましたが、メイクファイルのアプローチが私が言ったように機能しないので、これは実際には役に立ちません!@Dennis:わかりました。まず、指定された数を超えるプロセスを表示して、トップを実行します。次に、プロンプトに適切に戻りません。3番目に、ジョブを元に戻すことは正しくないと言いましたecho "DONE"。アクティブなジョブが完了する前に実行されたループの後にインジケーターを配置しただけです。=>これにより、仕事が終わっていないと思いました。
数学

回答:


15

メイクファイルあなたの問題に対する良い解決策です。この並列実行はシェルでプログラムできますが、気づいたように難しいです。makeの並列実装は、ジョブの開始と終了の検出だけでなく、負荷分散も処理しますが、これは注意が必要です。

グロビングの要件は障害ではありません。それをサポートするmake実装があります。GNU make、などのワイルドカード拡張機能$(wildcard *.c)やシェルアクセスなどがあります$(shell mycommand)(詳細については、GNU makeマニュアルの関数を参照してください)。これmakeはLinux のデフォルトであり、他のほとんどのシステムで利用可能です。以下は、ニーズに合わせて調整できるMakefileスケルトンです。

sources = $(wildcard * .src)

all:$(sources:.src = .tgt)

%.tgt:$ .src
    do_something $ <$$(derived_pa​​rams $ <)> $ @

make -j44つのジョブを並行して実行make -j -l3したり、負荷平均を約3にしたりするようなものを実行します。


8

あなたの派生した議論がどのようなものかわかりません。しかし、GNU Parallel http:// www.gnu.org/software/parallel/を使用すると、これを実行してCPUコアごとに1つのジョブを実行できます。

find . | parallel -j+0 'a={}; name=${a##*/}; upper=$(echo "$name" | tr "[:lower:]" "[:upper:]");
   echo "$name - $upper"'

あなたが導きたいものが単に.extensionを変更するだけなら、{。}が便利かもしれません:

parallel -j+0 lame {} -o {.}.mp3 ::: *.wav

http://www.youtube.com/watch?v=OpaiGYxkSuQで GNU Parallelの紹介ビデオを見る


7

シェルのwaitコマンドを使用するとうまくいきませんか?

for i in *
do
    do_something $i &
done
wait

ループはジョブを実行してから待機し、次のジョブを実行します。上記がうまくいかない場合、pwait後に移動するとあなたの方がうまくいくかもしれませんdone


100万個のファイルがあり、100万個のプロセスを実行していますか、それとも間違っていますか?
数学

1
@brubelsabs:ええ、100万プロセスを実行しようとします。質問で、処理に必要なファイルの数は言いませんでした。ネストされたforループを使用for file in *; do for i in {1..10}; do do_something "$i" & done; wait; doneして、それを制限する必要があると思います:(未テスト)一度に10を行い、各グループの10がすべて完了するまで待ってから、次の10を開始する必要があります。ループは一度に1つずつ実行し&ます。他のオプションについては、JRobertがリンクした質問を参照してください。Stack Overflowで、あなた(およびその質問)に似た他の質問を検索します。
追って通知があるまで一時停止します。

OPが数百万のファイルを予期している場合、彼はに問題がありfor i in *ます。彼はパイプまたは何かでループに引数を渡す必要があります。次に、内部ループの代わりに、増分カウンターを実行し"micro-"wait"-s"、「$((i%32))」ごとに実行することができます-eq '0'

@DennisWilliamson:wait内側のカウンターループと組み合わせるとうまくいきました。ありがとう!
ジョエルプラ

3

なぜ誰もxargsに言及していないのですか?

ちょうど3つの引数があると仮定すると、

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; done | xargs -n 3 -P $PROCS do_something

それ以外の場合は、区切り文字を使用します(nullが便利です):

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; echo -ne "\0"; done | xargs -0 -n 1 -P $PROCS do_something

編集:上記の場合、各パラメーターはヌル文字で区切る必要があり、パラメーターの数はxargs -nで指定する必要があります。


はい、私たちのプロジェクトでは誰かが同じ考えを持っていて、MSysを搭載したWindowsでもうまく機能します。
数学

0

いくつかの答えを試しました。スクリプトは、必要以上に複雑になります。理想的には、parallelまたはxargsを使用することをお勧めしますが、forループ内の操作が複雑な場合は、大きくて長い行ファイルを作成して並列に供給することが問題になる可能性があります。代わりに、次のようにソースを使用できます

# Create a test file 
$ cat test.txt
task_test 1
task_test 2

# Create a shell source file 
$ cat task.sh
task_test()
{
    echo $1
}

# use the source under bash -c 
$ cat test.txt | xargs -n1 -I{} bash -c 'source task.sh; {}'
1
2

したがって、あなたの問題の解決策は次のようになります

for i in *.myfiles; echo " do_something $i `derived_params $i` other_params
" >> commands.txt ; done

定義する do_something.sh

do_something(){
process $1
echo $2 
whatever $3 

}

xargまたはで実行するgnu parallel

   cat commands.txt | xargs -n1 -I{} -P8 bash -c 'source do_something.sh; {}'

forの反復の機能的な独立性が暗示されていると仮定します。

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