ディレクトリに PNG 画像が多数あります。これらの画像を圧縮するために実行する pngout というアプリケーションがあります。このアプリケーションは、私が作成したスクリプトによって呼び出されます。問題は、このスクリプトが一度に 1 つずつ、次のような処理を実行することです。
FILES=(./*.png)
for f in "${FILES[@]}"
do
echo "Processing $f file..."
# take action on each file. $f store current file name
./pngout -s0 $f R${f/\.\//}
done
一度に 1 つのファイルを処理するだけでも、かなりの時間がかかります。このアプリを実行すると、CPU 使用率は 10% に過ぎません。そこで、これらのファイルを 4 つのバッチに分割し、各バッチをディレクトリに配置して、4 つのターミナル ウィンドウから 4 つのプロセスを起動すると、スクリプトのインスタンスが 4 つになり、同時にこれらの画像が処理され、ジョブにかかる時間が 1/4 になることがわかりました。
2 番目の問題は、画像とバッチを分割し、スクリプトを 4 つのディレクトリにコピーし、4 つのターミナル ウィンドウを開き、などなど、時間を無駄にしてしまったことです。
何も分割せずに 1 つのスクリプトでそれを実行するにはどうすればよいでしょうか?
2 つのことを意味しています。まず、bash スクリプトからプロセスをバックグラウンドで起動するにはどうすればよいでしょうか。(最後に & を追加するだけですか?) 2 番目: 4 番目のタスクを送信した後、タスクをバックグラウンドに送信するのを停止し、タスクが終了するまでスクリプトを待機させるにはどうすればよいですか。つまり、1 つのタスクが終了すると、新しいタスクをバックグラウンドに送信し、常に 4 つのタスクを並列に保つということですか。そうしないと、ループによって無数のタスクがバックグラウンドで起動され、CPU が詰まってしまいます。
答え1
xargs
との並列実行をサポートするのコピーをお持ちの場合は-P
、次のようにするだけです。
printf '%s\0' *.png | xargs -0 -I {} -P 4 ./pngout -s0 {} R{}
その他のアイデアについては、Wooledge Bash wikiにセクションプロセス管理の記事で、あなたが望むことを正確に記述してください。
答え2
すでに提案されているソリューションに加えて、圧縮されていないファイルから圧縮ファイルを作成する方法を記述した makefile を作成し、それを使用してmake -j 4
4 つのジョブを並行して実行することができます。問題は、圧縮ファイルと圧縮されていないファイルに異なる名前を付けるか、それらを異なるディレクトリに保存する必要があることです。そうしないと、合理的な make ルールを記述することが不可能になります。
答え3
GNU Parallelをお持ちの場合http://www.gnu.org/software/parallel/インストールすると次の操作を実行できます:
parallel ./pngout -s0 {} R{} ::: *.png
GNU Parallel は、次の手順で簡単にインストールできます。
wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem
詳細については、GNU Parallel の紹介ビデオをご覧ください。 https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
答え4
あなたの2つの質問に答えます:
- はい、行末に & を追加すると、シェルにバックグラウンド プロセスを起動するように指示されます。
- コマンドを使用すると
wait
、シェルに、バックグラウンドで実行されているすべてのプロセスが終了するまで待機してから、さらに処理を進めるように指示できます。
以下に、j
バックグラウンド プロセスの数を追跡するために使用するように変更されたスクリプトを示します。NB_CONCURRENT_PROCESSES
に達すると、スクリプトはj
0 にリセットされ、すべてのバックグラウンド プロセスが終了するまで待機してから実行を再開します。
files=(./*.png)
nb_concurrent_processes=4
j=0
for f in "${files[@]}"
do
echo "Processing $f file..."
# take action on each file. $f store current file name
./pngout -s0 "$f" R"${f/\.\//}" &
((++j == nb_concurrent_processes)) && { j=0; wait; }
done