次のようなbashスクリプトがあるとしましょう。
echo "x" &
echo "y" &
echo "z" &
.....
echo "Z" &
wait
サブシェル/サブプロセスの終了コードを収集する方法はありますか?これを行う方法を探していますが、何も見つかりません。このサブシェルを並列に実行する必要があります。そうでなければ簡単になります。
私は〜を探しています一般的な解決策(並列に実行される未知/動的数の子プロセスがあります)。
答え1
使用wait
PIDを使用する場合する:
各プロセスIDで指定されたサブプロセスを待ちます。PIDまたは作業仕様作業仕様待っていた最後のコマンドの終了ステータスを終了して返します。
常に各プロセスのPIDを保存する必要があります。
echo "x" & X=$!
echo "y" & Y=$!
echo "z" & Z=$!
スクリプトでジョブ制御を有効にしてset -m
ジョブ仕様を使用することも%n
できますが、ほとんど望ましくありません。職業制御に他の多くの副作用がある。
wait
プロセスが完了したときと同じコードが返されます。wait $X
asを使用して、将来の(合理的な)時点で最終コードにアクセスしたり、$?
単に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
代わりに、各サブアイテムの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ファイルを削除することを忘れないでください。