【実務・中級編】【マルチタスク依存関係制御】WScript.Shell を用いた複数バッチ・スクリプトの実行順序制御タスクランナー – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【マルチタスク依存関係制御】WScript.Shell を用いた複数バッチ・スクリプトの実行順序制御タスクランナー

現場のエンジニアなら一度は直面する悪夢がある。
「夜間バッチ処理で、前段のデータ抽出スクリプトがコケたのに、後段の集計バッチが走りやがって、データベースのデータが盛大に破損した」――。

Windows環境において、タスクスケジューラや単純なバッチファイル(`.bat`)の連鎖は非常に脆弱だ。`call`コマンドや`start /wait`を並べただけのフローでは、エラーハンドリングは各スクリプトの職人芸に依存し、全体を俯瞰したトランザクションや依存関係(DAG: 有向非巡回グラフ)の制御など到底おぼつかない。

今回は、枯れ切った技術でありながら今なお現場のインフラを支える VBScript (WSH) を用い、堅牢かつアトミックな「複数プロセス依存関係制御タスクランナー」の設計手法を伝授する。非力な環境でも追加のランタイムなしで動作し、プロダクション環境に耐えうる堅牢なワークフローエンジンを構築しよう。

1. なぜ「単純なバッチの連鎖」は破綻するのか

多くの開発者が、以下のようなバッチファイルを組んで現場で爆死する。

@echo off
cscript //nologo extract.vbs
cscript //nologo transform.vbs
cscript //nologo load.vbs

このアプローチが非効率かつ危険な理由は明確だ。
1. 終了コード(Exit Code)の伝播漏れ: 前段がエラー(`Err.Number <> 0` や非ゼロの終了コード)を返しても、環境変数や設定次第で後段が平然と実行される。
2. ログの散逸: どのタスクが、どの時刻に、どのようなエラーコードで沈黙したのか、一元的なトレースができない。
3. リカバリー・スキップの欠如: 「特定のエラーであればリトライする」「このタスクが失敗したら後段をスキップして管理者へ通知する」といった条件分岐が書けない。

我々が目指すべきは、「タスクの定義」「依存関係」「エラーポリシー(リトライ/スキップ/アボート)」をコード外(設定)または厳格な構造体で分離し、一元管理する仕組みである。

2. 堅牢なタスクランナーのアーキテクチャ設計

今回構築するタスクランナーの要件は以下の通りだ。

  • 逐次実行と終了コードの監視: `WScript.Shell` の `Run` メソッドを使用し、プロセスの終了を完全に同期(同期実行:`bWaitOnReturn = True`)して、リターンコードを厳格にキャプチャする。
  • 依存関係の保証: 親タスクが正常終了(Exit Code = 0)した場合のみ、子タスクを実行する。
  • 構造化されたエラーハンドリング: VBScript特有の `On Error Resume Next` の罠を避け、プロセスレベルのエラーとスクリプトレベルの例外を明確に分離する。

3. プロダクションコード:依存関係制御タスクランナー

以下のコードは、設定された複数のスクリプトを依存関係順に評価・実行し、ログを残しながら安全にワークフローを完遂させるVBScriptの完全版である。そのまま `.vbs` として保存して実務で利用できる。

‘ ==============================================================================
‘ Script Name : WorkflowTaskRunner.vbs
‘ Description : WScript.Shellを用いた堅牢な複数スクリプト依存関係制御エンジン
‘ Author : Chief Systems Architect
‘ ==============================================================================

Option Explicit

‘ ログ出力用のファイルシステムオブジェクト
Dim objFSO, objLogFile
Set objFSO = CreateObject(“Scripting.FileSystemObject”)

‘ ログファイルのパス(スクリプトと同階層に生成)
Dim strLogPath
strLogPath = objFSO.BuildPath(objFSO.GetParentFolderName(WScript.ScriptFullName), “WorkflowExecution.log”)
Set objLogFile = objFSO.OpenTextFile(strLogPath, 8, True) ‘ 8 = ForAppending

‘ ログ記録プロシージャ
Sub WriteLog(strMessage, strLevel)
Dim strTimestamp
strTimestamp = Now()
Dim strLogEntry
strLogEntry = “[” & strTimestamp & “] [” & strLevel & “] ” & strMessage
WScript.Echo strLogEntry ‘ コンソール出力
objLogFile.WriteLine strLogEntry ‘ ファイル出力
End Sub

WriteLog “=== ワークフローエンジン起動 ===”, “INFO”

‘ ——————————————————————————
‘ タスク定義構造の模倣(多次元配列による定義)
‘ 形式:
‘ (0) タスクID
‘ (1) 実行コマンド / スクリプトパス
‘ (2) 前提となる依存タスクID (依存なしの場合は “”)
‘ (3) 失敗時の挙動 (“ABORT” = 即時中断, “IGNORE” = 無視して次へ)
‘ ——————————————————————————
Dim tasks(2, 3)

‘ タスク 1: データ抽出フェーズ
tasks(0, 0) = “TASK_EXTRACT”
tasks(0, 1) = “cscript //nologo 01_extract.vbs”
tasks(0, 2) = “”
tasks(0, 3) = “ABORT”

‘ タスク 2: データ変換フェーズ (TASK_EXTRACTの成功に依存)
tasks(1, 0) = “TASK_TRANSFORM”
tasks(1, 1) = “cscript //nologo 02_transform.vbs”
tasks(1, 2) = “TASK_EXTRACT”
tasks(1, 3) = “ABORT”

‘ タスク 3: データロードフェーズ (TASK_TRANSFORMの成功に依存)
tasks(2, 0) = “TASK_LOAD”
tasks(2, 1) = “cscript //nologo 03_load.vbs”
tasks(2, 2) = “TASK_TRANSFORM”
tasks(2, 3) = “ABORT”

‘ 実行ステータ스を保持するDictionary (タスクID -> 終了コード)
Dim objStatus
Set objStatus = CreateObject(“Scripting.Dictionary”)

Dim objShell
Set objShell = CreateObject(“WScript.Shell”)

Dim i, intResult, strTaskId, strCommand, strDependsOn, strOnError, bCanRun
Dim workflowFailed : workflowFailed = False

‘ ——————————————————————————
‘ メインループ:依存関係の解決と実行
‘ ——————————————————————————
For i = 0 To UBound(tasks, 1)
strTaskId = tasks(i, 0)
strCommand = tasks(i, 1)
strDependsOn = tasks(i, 2)
strOnError = tasks(i, 3)

bCanRun = True

‘ 依存関係の評価
If strDependsOn <> “” Then
If objStatus.Exists(strDependsOn) Then
If objStatus(strDependsOn) <> 0 Then
bCanRun = False
WriteLog “タスク [” & strTaskId & “] は依存タスク [” & strDependsOn & “] が失敗したためスキップされます。”, “WARN”
objStatus.Add strTaskId, -999 ‘ スキップコードを格納
End If
Else
bCanRun = False
WriteLog “タスク [” & strTaskId & “] の依存タスク [” & strDependsOn & “] が実行履歴に存在しません。”, “ERROR”
objStatus.Add strTaskId, -999
End If
End If

‘ 実行可能であればプロセスを起動
If bCanRun Then
WriteLog “タスク開始: ” & strTaskId & ” (Command: ” & strCommand & “)”, “INFO”

‘ WScript.Shell.Run(Command, WindowStyle, WaitOnReturn)
‘ 0: ウィンドウ非表示, True: プロセス終了までスクリプトをブロック
On Error Resume Next
intResult = objShell.Run(strCommand, 0, True)

If Err.Number <> 0 Then
WriteLog “OSレベルの実行例外発生: ” & Err.Description, “ERROR”
intResult = -1
Err.Clear
End If
On Error GoTo 0

objStatus.Add strTaskId, intResult

If intResult = 0 Then
WriteLog “タスク成功: ” & strTaskId & ” (ExitCode: 0)”, “INFO”
Else
WriteLog “タスク失敗: ” & strTaskId & ” (ExitCode: ” & intResult & “)”, “ERROR”

If strOnError = “ABORT” Then
WriteLog “エラーポリシー [ABORT] に従い、ワークフローを中断します。”, “FATAL”
workflowFailed = True
Exit For
Else
WriteLog “エラーポリシー [IGNORE] に従い、処理を継続します。”, “WARN”
End If
End If
End If
Next

‘ ——————————————————————————
‘ 終了処理
‘ ——————————————————————————
If workflowFailed Then
WriteLog “=== ワークフローは異常終了しました ===”, “FATAL”
objLogFile.Close
WScript.Quit 1
Else
WriteLog “=== ワークフローは正常終了しました ===”, “INFO”
objLogFile.Close
WScript.Quit 0
End If

4. プロフェッショナルが教える実装の勘所と注意点

上記のコードを現場に投入するにあたり、プロのアーキテクトとして押さえておくべき「罠」と「対策」を共有する。

① `WScript.Shell.Run` の第3引数(`bWaitOnReturn`)の絶対死守

非同期実行(`False`)にしてしまうと、OSがプロセスを起動した瞬間にVBScript側が次のタスクへ進んでしまう。依存関係制御を行うタスクランナーにおいて、`True`(同期実行)以外の選択肢は存在しない。 プロセスが完全に終了し、戻り値を返却するまで親スクリプトを確実にブロックさせよ。

② VBScriptの `On Error` 制御の境界線

VBScriptの最大の悪習は、雑に `On Error Resume Next` をグローバルに貼り巡らせることだ。これにより、致命的なオブジェクト生成エラーなどが闇に葬り去られる。
今回のコードでは、`objShell.Run` のOS例外をキャッチするピンポイントな箇所でのみエラーシールドを展開し、それ以外は厳格な例外検知を行っている。この規律を崩してはならない。

③ データベースやファイル連携における排他制御

各タスクから同一のデータベースやログファイルにアクセスする場合、プロセスが厳密に直列化(シリアライズ)されていることが前提となる。このタスクランナーによって「タスクの順序」は保証されるが、もし子スクリプト側がマルチスレッドやさらなる非同期処理を行っている場合、デッドロックやファイルロック競合(エラー `Permission Denied`)が発生する。
子スクリプト側でも、ファイルアクセスのリトライロジック(トランザクショナルな設計)を必ず担保すること。

総括

VBScriptはレガシーな技術と揶揄されることもあるが、Windows環境において「追加のインストーラーや重いランタイムを一切必要とせず、OS標準の機能だけで極めて軽量にインフラを制御できる」という圧倒的なアドバンテージは色褪せない。

今回紹介した依存関係制御タスクランナーをベースにすれば、日々の複雑なデータ連携バッチ、ファイル同期、システムメンテナンスの自動化を、極めて堅牢かつ透明性の高い形で維持管理できるようになるはずだ。設計の美しさと堅牢性を、ぜひ実際の現場で体感してほしい。

タイトルとURLをコピーしました