シェルで待ち時間のないジョブネットを作った
次の要件でジョブネットを作るとする。
・ジョブネットを構成するジョブは、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コードのステップに到達するまでに全てのジョブは開始済みとなっているので、もはや開始前の待ち時間を考慮する必要はない。