bashから2つのプロセスを作成します。いずれかのプロセスが終了すると、両方のプロセスが終了します。

bashから2つのプロセスを作成します。いずれかのプロセスが終了すると、両方のプロセスが終了します。

Bashで2つのプロセスを作成しました。これら2つのプロセスは相互依存的です。どちらかが死んだら、どちらもやめたいです。最もきれいな方法は何ですか?現在私は以下を持っています:

# start process a
/bin/program_a;
a_pid=$!

# start process b
/bin/program_b;
b_pid=$!

# kill process b if process a exits
wait $a_pid
echo "a_pid died, killing process b"
kill -9 $b_pid

ただし、これはプロセスaが終了した場合にのみプロセスbを終了するのに役立ちます。プロセスbが終了したら、プロセスを終了する方法は?

答え1

そしてzsh

pids=()
trap '
  trap - CHLD
  (($#pids)) && kill $pids 2> /dev/null
' CHLD

sleep 2 & pids+=$!
sleep 1 & pids+=$!
sleep 3 & pids+=$!

wait

(ここではsleepテストコマンドとして使用されます)

bashCHLDトラップは、このオプションがオンのときにのみ実行されるようです。mこのオプションでは、ジョブは別のプロセスグループで実行されるため、ジョブを開始したくありません。また、ハンドラ内でハンドラをリセットすることはbashでは機能しないようです。したがって、bashこれに対応する内容は次のとおりです。

pids=()
gotsigchld=false
trap '
  if ! "$gotsigchld"; then
    gotsigchld=true
    ((${#pids[@]})) && kill "${pids[@]}" 2> /dev/null
  fi
' CHLD

sleep 2 & pids+=("$!")
sleep 1 & pids+=("$!")
sleep 3 & pids+=("$!")

set -m
wait
set +m

答え2

私がテストしたシェルの中で私が知っている限り、3つのシェルは、と、との面SIGCHLDでほぼ正しいことをします。ご覧のとおり、信号ハンドラを設定するときに割り込みが可能でなければなりません。waityashdashmkshwaitwait()、ㅏsleep()またはread()持ち運べるsleep()(もちろん、以前の呼び出しで割り込みが発生した場合は奇妙に動作しますが、alarm())。どの(ブロックされていない/無視される)信号はwait()

私の考えでは、これらのもののシェル実装はそれほど変わらないはずですが...いくつかは異なります。特に、、、、、、bashの最悪の様子を見せてくれます。そしてbashksh93dashmkshyashzshzshksh93 ほぼ次のシーケンスは正しく実行されますが、最初の終了プロセスの終了状態は維持されません。ひどいものではありません。最後に終了したPIDをzsh使用するように求められたことについて不平を言うこともあります。wait

これが私がしたことです:

unset IFS
script=$(cat <<""
        PS4="$0 + "                                           
        trap '  for p                                     ### loop over bgd pids
                do      shift                             ### clear current pid
                        if      kill -0 "$p" 2>/dev/null  ### still running?
                        then    set --  "$@" "$p"         ### then append again
                        else    wait    "$p"              ### else get return
                                exit    "$(kill "$@")$?"  ### kill others; exit
                        fi
                done'   CHLD                              ### wait til CHLD
        for n   in      $(shuf -i 3-7)                    ### randomize order
        do      (sleep "$n";exit "$n")& set "$@" "$!"     ### sleep 3 exits 3
        done;   set -x; wait                              ### debug, wait

)

上記のコードは、シェルが返されるとすぐに残っているすべてのバックグラウンドサブシェルを終了するだけでなく、最初の返されたサブシェルの終了コードを親シェルの終了コードに伝播する必要があります。それしなければならないwaitまだ待機していない子プロセスを呼び出すと、バックグラウンドプロセスの終了ステータスがすぐに返される必要があるために機能します。 SIGCHLDは最初の信号を終了する信号なので、wait2番目の信号をwait表示する必要があります。最初最初に返された子孫が待たなければならない実際の時間。少なくとも簡単にはそうする必要があります。しかし、シェル実装が複雑になるほど、このロジックの信頼性は低下するようです。

$scriptこれを行うときに各シェルを実行する方法は次のとおりです。

for sh in yash zsh ksh bash mksh dash
do  time  "$sh" +m -c "$script"                           ### no job control
done

bash3秒以内に終了しない唯一のシェルです。zshそしてksh93両方(私の考えではこれは間違っているようです) exit 0、そうでない場合は、3秒以内に終了してください。exit 33秒以内に他のもの。テスト結果は次のとおりです。

yash + wait
yash + shift
yash + wait 19111
yash + kill 19112 19113 19116 19117
yash + exit 3

real    0m3.013s
user    0m0.007s
sys     0m0.000s

zsh + wait
zsh + p=19124
zsh + shift
zsh + kill -0 19124
zsh + set -- 19125 19127 19129 19132 19124
zsh + p=19125
zsh + shift
zsh + kill -0 19125
zsh + wait 19125
zsh:wait:12: pid 19125 is not a child of this shell
zsh + kill 19127 19129 19132 19124
zsh + exit 0

real    0m3.023s
user    0m0.017s
sys     0m0.000s

ksh + wait
ksh + shift
ksh + kill -0 19137
ksh + 2> /dev/null
ksh + set -- 19138 19139 19140 19141 19137
ksh + shift
ksh + kill -0 19138
ksh + 2> /dev/null
ksh + wait 19138
ksh + kill 19139 19140 19141 19137
ksh + exit 0

real    0m3.018s
user    0m0.000s
sys     0m0.010s

bash + wait

real    0m7.018s
user    0m0.007s
sys     0m0.007s

mksh + wait
mksh + shift
mksh + 2>/dev/null 
mksh + kill -0 19157
mksh + set -- 19158 19159 19160 19161 19157
mksh + shift
mksh + 2>/dev/null 
mksh + kill -0 19158
mksh + set -- 19159 19160 19161 19157 19158
mksh + shift
mksh + 2>/dev/null 
mksh + kill -0 19159
mksh + set -- 19160 19161 19157 19158 19159
mksh + shift
mksh + 2>/dev/null 
mksh + kill -0 19160
mksh + set -- 19161 19157 19158 19159 19160
mksh + shift
mksh + 2>/dev/null 
mksh + kill -0 19161
mksh + wait 19161
mksh + kill 19157 19158 19159 19160
mksh + exit 3

real    0m3.022s
user    0m0.003s
sys     0m0.000s

dash + wait
dash + shift
dash + kill -0 19165
dash + set -- 19166 19168 19170 19173 19165
dash + shift
dash + kill -0 19166
dash + wait 19166
dash + kill 19168 19170 19173 19165
dash + exit 3

real    0m3.008s
user    0m0.000s
sys     0m0.000s

関連情報