並列バックグラウンドプロセス(サブシェル)の終了コードの収集

並列バックグラウンドプロセス(サブシェル)の終了コードの収集

次のようなbashスクリプトがあるとしましょう。

echo "x" &
echo "y" &
echo "z" &
.....
echo "Z" &
wait

サブシェル/サブプロセスの終了コードを収集する方法はありますか?これを行う方法を探していますが、何も見つかりません。このサブシェルを並列に実行する必要があります。そうでなければ簡単になります。

私は〜を探しています一般的な解決策(並列に実行される未知/動的数の子プロセスがあります)。

答え1

使用waitPIDを使用する場合する:

各プロセスIDで指定されたサブプロセスを待ちます。PIDまたは作業仕様作業仕様待っていた最後のコマンドの終了ステータスを終了して返します。

常に各プロセスのPIDを保存する必要があります。

echo "x" & X=$!
echo "y" & Y=$!
echo "z" & Z=$!

スクリプトでジョブ制御を有効にしてset -mジョブ仕様を使用することも%nできますが、ほとんど望ましくありません。職業制御に他の多くの副作用がある

waitプロセスが完了したときと同じコードが返されます。wait $Xasを使用して、将来の(合理的な)時点で最終コードにアクセスしたり、$?単にtrue / falseとして使用したりできます。

echo "x" & X=$!
echo "y" & Y=$!
...
wait $X
echo "job X returned $?"

waitコマンドがまだ完了していない場合は、コマンドが完了するまで一時停止します。

このような低迷を避けたい場合は、trapオンに設定SIGCHLD、終了回数を計算し、すべてがwait完了するとすぐにすべてのタスクを処理します。waitほぼ常に一人で使用できます。

答え2

HandleJobsを使用したAlexander Millsの回答は良い出発点を提供しましたが、このエラーも発生しました。

警告: run_pending_traps: Trap_list[17] の無効な値: 0x461010

bash競争条件の問題かもしれません。

代わりに、各サブアイテムのpidを保存してから待ってから、各サブアイテムの終了コードを具体的に取得します。私は関数から子プロセスを作成し、子プロセスを待ちたい親プロセスを待つリスクを避けるためのよりきれいな方法を見つけました。トラップを使用していなかったので、何が起こったのかをより明確にしました。

#!/usr/bin/env bash

# it seems it does not work well if using echo for function return value, and calling inside $() (is a subprocess spawned?) 
function wait_and_get_exit_codes() {
    children=("$@")
    EXIT_CODE=0
    for job in "${children[@]}"; do
       echo "PID => ${job}"
       CODE=0;
       wait ${job} || CODE=$?
       if [[ "${CODE}" != "0" ]]; then
           echo "At least one test failed with exit code => ${CODE}" ;
           EXIT_CODE=1;
       fi
   done
}

DIRN=$(dirname "$0");

commands=(
    "{ echo 'a'; exit 1; }"
    "{ echo 'b'; exit 0; }"
    "{ echo 'c'; exit 2; }"
    )

clen=`expr "${#commands[@]}" - 1` # get length of commands - 1

children_pids=()
for i in `seq 0 "$clen"`; do
    (echo "${commands[$i]}" | bash) &   # run the command via bash in subshell
    children_pids+=("$!")
    echo "$i ith command has been issued as a background job"
done
# wait; # wait for all subshells to finish - its still valid to wait for all jobs to finish, before processing any exit-codes if we wanted to
#EXIT_CODE=0;  # exit code of overall script
wait_and_get_exit_codes "${children_pids[@]}"

echo "EXIT_CODE => $EXIT_CODE"
exit "$EXIT_CODE"
# end

答え3

他の答えは不必要に複雑だと思います。

背景PIDを起動したときに配列に保存すると、それを明示的に待ち、2番目の配列から戻りコードを収集できます。

pids=()
echo "x" & pids+=($!)
echo "y" & pids+=($!)
echo "z" & pids+=($!)
....
echo "Z" & pids+=($!)

# wait and collect return codes
rets=()
for pid in ${pids[*]}; do
    wait $pid
    rets+=($?)
done
echo "Return codes: ${rets[*]}"

戻りコードの収集同じ順序で職業の導入とともに。

1つ以上の操作が失敗したことを確認したい場合は、すべての戻りコードを収集する必要はありません。

error=false
for pid in ${pids[*]}; do
    if ! wait $pid; then
        error=true
    fi
done

答え4

コマンドを識別する良い方法がある場合は、終了コードをtmpファイルに印刷してから、関心のある特定のファイルにアクセスできます。

#!/bin/bash

for i in `seq 1 5`; do
    ( sleep $i ; echo $? > /tmp/cmd__${i} ) &
done

wait

for i in `seq 1 5`; do # or even /tmp/cmd__*
    echo "process $i:"
    cat /tmp/cmd__${i}
done

tmpファイルを削除することを忘れないでください。

関連情報