web-dev-qa-db-ja.com

並列シェルループ

多くのファイルを処理したいのですが、ここにたくさんのコアがあるので、並行して処理したいと思います。

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

Makefile solution を知っていますが、コマンドにはシェルグロブリストからの引数が必要です。私が見つけたのは:

> 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メーリングリストでの議論は今ではとても古いので、これがまだ行われていないとは信じられません。それで、あなたはもっとよく知っていますか?

11
math

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

グロブの要件は障害ではありません。それをサポートするmake実装があります。 GNU make、$(wildcard *.c)などのワイルドカード拡張と$(Shell mycommand)などのシェルアクセス(GNU詳細についてはmanualを作成してください)。これはLinuxではデフォルトのmakeであり、他のほとんどのシステムで使用できます。ニーズに適応できる可能性のあるMakefileスケルトンは次のとおりです。

 sources = $(wildcard * .src)
 
 all:$(sources:.src = .tgt)
 
%。tgt: $ .src 
 do_something $ <$$(derivated_pa​​rams $ <)> $ @ 

make -j4のようなものを実行して4つのジョブを並行して実行するか、make -j -l3のようなものを実行して負荷の平均を約3に保ちます。

派生した引数がどのようなものかわかりません。しかし、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

GNU Parallel at http://www.youtube.com/watch?v=OpaiGYxkSuQ の紹介ビデオをご覧ください

8
Ole Tange

シェルのwaitコマンドを使用してもうまくいきませんか?

for i in *
do
    do_something $i &
done
wait

ループはジョブを実行して待機し、次のジョブを実行します。上記がうまくいかない場合は、pwaitの後にdoneを移動するとうまくいく可能性があります。

なぜ誰も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で指定する必要があります。

3
zebediah49

私はいくつかの答えを試しました。これらは、スクリプトを必要以上に複雑にします。理想的には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の反復の機能的独立性が暗示されていると思います。

0
vegabondx