技術メモ

役に立てる技術的な何か、時々自分用の覚書。幅広く色々なことに興味があります。

シェルスクリプトでFOR文でバックグラウンドで起動したの並列処理を待機する方法

# ジョブ ID を格納する配列
job_ids=()

array=(2 5 4 7 2 1)
for i in ${array[@]}; do
    
    # ジョブをバックグラウンドで実行し、ジョブ ID を取得
    (sleep $i && echo $i) &
    
    # 直前にバックグラウンドで起動したプロセスの ID を取得
    job_ids+=($!)
done

# すべてのジョブが終了するまで待機
for job_id in "${job_ids[@]}"; do
    wait "${job_id}" || exit 1
done

# 後続処理を記述
echo "Finish!"

結果

 » sh sample.sh
1
2
2
4
5
7
Finish!

■例
あるフォルダのすべてのファイルに対して並列で時間のかかる処理を実行し、並列処理が完了したら後続処理をしたいという場合

input_dir=$1

# ジョブ ID を格納する配列
job_ids=()

for file in "${input_dir}/*"; do
    
    # first.sh(時間がかかる処理)をバックグラウンドで実行し、ジョブ ID を取得
    (sh first.sh "${file}") &
    
    # 直前にバックグラウンドで起動したプロセスの ID を取得
    job_ids+=($!)
done

# すべてのジョブが終了するまで待機
for job_id in "${job_ids[@]}"; do
    wait "${job_id}" || exit 1
done

# 後続処理を記述
sh second.sh

補足

job_ids=()

この書き方ではbash特有の記法になる。
POSIX準拠の記法は以下のようになる。

# POSIXシェルでは配列を使わず、代わりに一時ファイルを使用
job_ids_file=$(mktemp)

for file in "${input_dir}/*"; do
    # first.sh(時間がかかる処理)をバックグラウンドで実行し、ジョブ ID を一時ファイルに書き込む
    (sh first.sh "${file}") &
    echo $! >> "$job_ids_file"
done

# 生成したジョブIDの一覧を読み込み、それぞれのジョブが終了するのを待つ
while IFS= read -r job_id; do
    wait "${job_id}" || exit 1
done < "$job_ids_file"

# 一時ファイルを削除
rm "$job_ids_file"

# 後続処理を記述
sh second.sh