Bashスクリプトは限られた数のコマンドを並行して処理します


196

次のようなbashスクリプトがあります。

#!/bin/bash
wget LINK1 >/dev/null 2>&1
wget LINK2 >/dev/null 2>&1
wget LINK3 >/dev/null 2>&1
wget LINK4 >/dev/null 2>&1
# ..
# ..
wget LINK4000 >/dev/null 2>&1

しかし、コマンドが完了するまで各行を処理してから次の行に移動するのは非常に時間がかかります。たとえば、一度に20行を処理し、終了したらさらに20行処理したいと思います。

wget LINK1 >/dev/null 2>&1 &コマンドをバックグラウンドに送信して続行することを考えましたが、ここには4000行あります。これは、パフォーマンスの問題が発生することを意味します。同時に開始する必要があるプロセスの数に制限があるので、これは良いことではありません考え。

私が今考えている解決策の1つは、コマンドの1つがまだ実行されているかどうかを確認することです。たとえば、20行後、次のループを追加できます。

while [  $(ps -ef | grep KEYWORD | grep -v grep | wc -l) -gt 0 ]; do
sleep 1
done

もちろんこの場合、行末に&を追加する必要があります。しかし、これは正しい方法ではないと感じています。

では、実際に各20行をグループ化し、それらが完了するのを待ってから次の20行に進む方法を教えてください。このスクリプトは動的に生成されるため、生成中に必要な計算をすべて実行できますが、その必要はありませんwgetを使用してください。これは単なる例であるため、wget固有のソリューションはどれも役に立ちません。


1
waitここで正解ですが、proctoolsを使用して、つまり、特定の名前のプロセスがまだ実行されているかどうかを確認する正当な理由がある場合は、while [ $(ps …より適切に記述されます。while pkill -0 $KEYWORD…
kojiro 2013年

この質問は再開する必要があると思います。「可能性のある重複」QAとは、有限数のプログラムを並行して実行することです。2-3コマンドのように。ただし、この質問は、ループなどでコマンドを実行することに焦点を当てています。(「4000行あります」を参照)。
VasiliNovikov

@VasyaNovikov この質問と重複の両方に対するすべての回答を読みました か?ここでのこの質問に対するすべての答えは、重複する質問への回答にもあります。それがまさに、重複する質問の定義です。コマンドをループで実行しているかどうかに関係なく、まったく違いはありません。
robinCTS

@robinCTS交差点がありますが、質問自体は異なります。また、リンクされたQAで最も人気のある回答のうち6つは2つのプロセスのみを扱います。
VasiliNovikov

2
3年前の質問ですが、リンクされた質問の回答よりも回答がより明確で、明確で、優れており、非常に賛成されているため、この質問を再開することをお勧めします。
Dan Nissenbaum

回答:


331

wait組み込みを使用:

process1 &
process2 &
process3 &
process4 &
wait
process5 &
process6 &
process7 &
process8 &
wait

上記の例では、4つのプロセスprocess1... process4がバックグラウンドで開始され、シェルはそれらが完了するまで待機してから次のセットを開始します。

GNUマニュアルから:

wait [jobspec or pid ...]

各プロセスID pidまたはジョブ仕様jobspecで指定された子プロセスが終了するまで待機し、最後に待機したコマンドの終了ステータスを返します。ジョブ仕様が指定されている場合、ジョブ内のすべてのプロセスが待機されます。引数を指定しないと、現在アクティブなすべての子プロセスが待機し、戻りステータスはゼロになります。jobspecもpidもシェルのアクティブな子プロセスを指定していない場合、戻りステータスは127です。


14
だから、基本的にはi=0; waitevery=4; for link in "${links[@]}"; do wget "$link" & (( i++%waitevery==0 )) && wait; done >/dev/null 2>&1
小次郎

18
各プロセスが正確に同時に完了することが確実でない限り、これは悪い考えです。現在の合計ジョブを特定の上限に保つには、新しいジョブを開始する必要があります.... 並列が答えです
rsaw 14

1
これをループで行う方法はありますか?
DomainsFeatured

私はこれを試しましたが、あるブロックで行われた変数の割り当ては次のブロックでは利用できないようです。これは、それらが別々のプロセスであるためですか?変数をメインプロセスに戻す方法はありますか?
ボビー

97

parallelを参照してください。構文はに似てxargsいますが、コマンドを並行して実行します。


13
wait新しいジョブを開始する前にバッチ全体が完了するのを待つのではなく、古いジョブが完了すると新しいジョブを開始するので、これはを使用するよりも優れています。
chepner 2013年

5
たとえば、ファイルにリンクのリストがある場合、一度にcat list_of_links.txt | parallel -j 4 wget {}4つwgetのを実行し続けることができます。
Llama氏、2015

5
の代わりとなるpexecと呼ばれる新しい子供が町にいparallelます。
スラッシュビン2015年

2
例を提供することはより役立つでしょう
jterm

1
parallel --jobs 4 < list_of_commands.shここで、list_of_commands.shは、すべての行に単一のコマンド(たとえばwget LINK1、なしのメモ&)を含むファイルです。行う必要があるかもしれませんCTRL+Zし、bg後にバックグラウンドで実行されている、それを残して。
weiji14

71

実際に、コマンドを並行して実行xargs できます。そのための特別な-P max_procsコマンドラインオプションがあります。を参照してくださいman xargs


2
+100組み込みで非常に使い
クレイ

追加のパッケージ/依存関係が必要ないので、小さなコンテナーに使用するのに最適です!
Marco Roy、

1
:例については、この質問を参照してくださいstackoverflow.com/questions/28357997/...
マルコ・ロイ

7

20個のプロセスを実行し、次のコマンドを使用できます。

wait

バックグラウンドジョブがすべて完了すると、スクリプトは待機して続行します。

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