バックグラウンドプロセス2個以上をwaitして実行結果を取得する際のメモです。Linux(RHEL7、CentOS7)で確認済みですが、Unixでも同じことが言えるはずです。
まずは間違っている例です。下記の様に、プロセスIDを取得して、waitする際に2個のプロセスIDを指定してしまうと、誤った結果が得られます。(最後に指定したプロセスIDの結果のみが取得できる。)
$ cat parent.sh
#!/bin/sh
./child.sh $1 &
child1p=$!
./child.sh $2 &
child2p=$!
sleep 3
wait $child1p $child2p
childr=$?
echo "child1p:$child1p, child2p:$child2p, childr:$childr"
$ cat child.sh
#!/bin/sh
sleep 1
exit $1
下記が実行結果です。
$ time ./parent.sh 1 1
child1p:10159, child2p:10160, childr:1
real 0m3.005s
user 0m0.005s
sys 0m0.003s
$ time ./parent.sh 0 0
child1p:10165, child2p:10166, childr:0
real 0m3.005s
user 0m0.004s
sys 0m0.003s
$ time ./parent.sh 0 1
child1p:10171, child2p:10172, childr:1
real 0m3.005s
user 0m0.001s
sys 0m0.006s
うまくいくやん! と、思われ方。要注意! 下記を実行すれば誤りに気付きます。
$ time ./parent.sh 1 0
child1p:10152, child2p:10153, childr:0
real 0m3.006s
user 0m0.004s
sys 0m0.004s
引数pidを指定した場合,waitコマンドは最後の完了を待ったプロセスのコマンドの終了コードで終了します。
http://itdoc.hitachi.co.jp/manuals/3021/3021313320/JPAS0399.HTM
正しい実装は下記。それぞれ個別にwaitする必要がある。
$ cat parent2.sh
#!/bin/sh
./child.sh 0 &
child1p=$!
./child.sh 1 &
child2p=$!
sleep 3
wait $child1p
child1r=$?
wait $child2p
child2r=$?
echo "child1p:$child1p, child2p:$child2p, child1r:$child1r, child2r:$child2r"
ん? と思われた方。そう、子プロセスが[sleep 1]しているにも関わらず、親プロセスで[sleep 3]しているため、waitにたどり着くまでに子プロセスは終了しています。「終了したプロセスの実行結果なんて取得できるの?」と。
waitコマンドについて詳しく言及した記事はありませんでしたが、waitシステムコールについて調べると、ありました。
終了したが、wait されていない子プロセスは「ゾンビ」になる。後で親プロセスが wait を実行して子プロセスについての情報を取得できるように、カーネルはゾンビプロセスについて最小限の情報 (PID、終了ステータス、 リソース使用状況) を保持する。ゾンビプロセスは、 wait によってシステムから削除されない限り、カーネルのプロセステーブルの 1 エントリーを消費する。このプロセステーブルが 一杯になると、新たにプロセスを作ることができなくなる。親プロセスが終了すると、その親プロセスの「ゾンビ」の子プロセスは (もしあれば) init(1) の養子となる。 init(1) は wait を自動的に実行し、ゾンビを削除する。
https://linuxjm.osdn.jp/html/LDP_man-pages/man2/wait.2.html
「養子」という表現、かなり好きです。(そこではない) つまり、子プロセスの実行結果は、親プロセスが終了するまでは間違いなく保持されるようです。そのため、上記コードは問題なしです。
$ time ./parent2.sh 1 0
child1p:10185, child2p:10186, child1r:0, child2r:1
real 0m3.006s
user 0m0.004s
sys 0m0.004s