シェルで待ち時間のないジョブネットを作った

次の要件でジョブネットを作るとする。
・ジョブネットを構成するジョブは、jobA, jobB, jobC, jobD とする。
・jobAとjobBが終了したらjobDを動かす。
・ジョブは可能な限り並列(パラレル)で動かす。
・待ち時間をなくす。
・ジョブが一つでも異常終了したらジョブネットも異常終了させる。

これはそんなに難しくなく、 例えば以下コードで実現できる。

 1#!/bin/bash
 2
 3./jobA &
 4pidA=$!
 5./jobB &
 6pidB=$!
 7./jobC &
 8pidC=$!
 9
10wait $pidA
11stsA=$?
12if [ $stsA != 0 ];then
13    exit 1
14fi
15wait $pidB
16stsB=$?
17if [ $stsB != 0 ];then
18    exit 1
19fi
20
21./jobD &
22pidD=$!
23
24wait $pidC
25stsC=$?
26if [ $stsC != 0 ];then
27    exit 1
28fi
29
30wait $pidD
31stsD=$?
32if [ $stsD != 0 ];then
33    exit 1
34fi
35
36exit 0

ここで要件を以下のように変更してみる。(赤字が追加箇所)
・ジョブネットを構成するジョブは、jobA, jobB, jobC, jobD, jobE とする。
・jobAとjobBが終了したらjobDを動かす。
jobBとjobCが終了したらjobEを動かす。
・ジョブは可能な限り並列(パラレル)で動かす。
・待ち時間をなくす。
・ジョブが一つでも異常終了したらジョブネットも異常終了させる。

一見、簡単に実現できそうだが、これが難しい。
単純に上記コードを少し修正するだけだと、jobDとjobEのいずれかに実行開始前の待ち時間が発生するからだ。
waitコマンドを使うことが、待ち時間発生の原因となる。

そこで根本から見直して実現したのが以下だ。
前提条件として、各ジョブは自身の終了ステータスをファイル出力しているものとする。
例えば、jobAは自身の終了ステータスをjobA.stsというファイルに出力する。
waitを使わず、代わりにpsコマンドをループ実行させることで、jobA, jobB, jobC の稼働状況をチェックしている。
また、ジョブが出力したファイル内容を見てステータス判定している。

 1#!/bin/bash
 2
 3rm -f ./jobA.sts; rm -f ./jobB.sts; rm -f ./jobC.sts; rm -f ./jobD.sts; rm -f ./jobE.sts
 4
 5./jobA &
 6pidA=$!
 7./jobB &
 8pidB=$!
 9./jobC &
10pidC=$!
11
12finA=9; finB=9; finC=9
13stsA=9; stsB=9; stsC=9
14pidD=0; pidE=0
15
16while :
17do
18    if [ $finA != 1 ]; then
19        finA=`ps -p $pidA >/dev/null 2>&1; echo $?`
20    elif [ $finA = 1 ];then
21        stsA=`cat ./jobA.sts`
22        if [ $stsA != 0 ];then
23            echo "jobA error!"
24            exit 1
25        fi
26    fi
27    if [ $finB != 1 ]; then
28        finB=`ps -p $pidB >/dev/null 2>&1; echo $?`
29    elif [ $finB = 1 ];then
30        stsB=`cat ./jobB.sts`
31        if [ $stsB != 0 ];then
32            echo "jobB error!"
33            exit 1
34        fi
35    fi
36    if [ $pidD = 0 ] && [ $stsA = 0 ] && [ $stsB = 0 ]; then
37        ./jobD &
38        pidD=$!
39    fi
40    if [ $finC != 1 ]; then
41        finC=`ps -p $pidC >/dev/null 2>&1; echo $?`
42    elif [ $finC = 1 ];then
43        stsC=`cat ./jobC.sts`
44        if [ $stsC != 0 ];then
45            echo "jobC error!"
46            exit 1
47        fi
48    fi
49    if [ $pidE = 0 ] && [ $stsB = 0 ] && [ $stsC = 0 ]; then
50        ./jobE &
51        pidE=$!
52    fi
53    # jobD, jobEを実行したらループを抜ける
54    if [ $pidD != 0 ] && [ $pidE != 0 ]; then
55        break
56    fi
57done
58
59wait $pidD
60if [ $? != 0 ]; then
61    echo "jobD error!"
62    exit 1
63fi
64wait $pidE
65if [ $? != 0 ]; then
66    echo "jobE error!"
67    exit 1
68fi
69
70exit 0

なお、最後の方のjobD, jobEについては、waitを使用した。
このwaitコードのステップに到達するまでに全てのジョブは開始済みとなっているので、もはや開始前の待ち時間を考慮する必要はない。

関連ページ