VBScriptを掌握せよ:WScript.Shellの「Run」と「Exec」を使いこなす極限の設計指針
業務自動化の現場において、VBScriptは「枯れた技術」と侮られがちだ。しかし、Windowsのネイティブ環境でこれほど軽量かつ強力な武器は他にない。特に外部コマンド(cmd.exeやPowerShell)との連携は、RPAツールが重すぎて動かないような極限の環境下で真価を発揮する。
だが、多くのエンジニアが `WScript.Shell.Run` と `Exec` の境界線を曖昧にしたままコードを書き、予期せぬフリーズやデッドロックに沈んでいる。今日は、この「外部プロセス制御」という深淵を、プロの設計思想で攻略する。
—
1. 原則:Run と Exec の明確な分断
まず、この二つのメソッドは役割が根本的に異なる。ここを混同してはならない。
- `Run` (実行して、終わり)
- 特徴: 非同期実行(`bWaitOnReturn` を `True` にすれば同期)、戻り値(ExitCode)のみ取得可能。
- 用途: バッチファイルの呼び出し、単純なファイル操作、戻り値による分岐。
- 限界: 標準出力を取得できない。
- `Exec` (実行して、捕まえる)
- 特徴: 同期実行のみ(※非同期的に進むが、制御はスクリプトに戻る)。標準出力・標準エラー出力をストリームとして取得可能。
- 用途: ログ解析、複雑なコマンド結果の取り込み、進捗のリアルタイム表示。
- 注意: ストリームのバッファを読み切らないと、呼び出し側のパイプが詰まりデッドロックを引き起こすリスクがある。
—
2. 【Run法】堅牢な戻り値判定のテンプレート
「とりあえずコマンドを叩いて、成功したか失敗したかを知りたい」場合は、`Run` を使う。ここで重要なのは、コマンドの終了を待機させ、かつウインドウを非表示にすることだ。
‘ コマンド実行のベストプラクティス
Dim shell, exitCode
Set shell = CreateObject(“WScript.Shell”)
‘ 第1引数: コマンド文字列
‘ 第2引数: 0=非表示, 1=通常, 7=最小化など
‘ 第3引数: True=終了まで待機, False=非同期
exitCode = shell.Run(“cmd.exe /c “”C:\path\to\script.bat”””, 0, True)
If exitCode = 0 Then
‘ 正常系
Else
‘ 異常系: エラーログへの記録など
WScript.Echo “Error occurred with code: ” & exitCode
End If
—
3. 【Exec法】標準出力を制する真のエンジニアリング
コマンドの結果を解析したい場合、`Exec` を選択する。ここで最も多い失敗が、`.StdOut.ReadAll()` をいきなり呼び出し、コマンドの出力が大きすぎてプロセスが停止するケースだ。
プロダクションレベルでは、以下の設計が推奨される。
Dim shell, objExec, outputLine
Set shell = CreateObject(“WScript.Shell”)
‘ PowerShellを使ってJSONやログを取得する例
Set objExec = shell.Exec(“powershell -Command “”Get-Service | Select-Object -First 5″””)
‘ ストリームが終了するまでループで読み込む(デッドロック回避)
Do While objExec.Status = 0
‘ 多少の待機(CPU負荷を抑える)
WScript.Sleep 100
Loop
‘ 全て読み込む
If Not objExec.StdOut.AtEndOfStream Then
outputLine = objExec.StdOut.ReadAll
WScript.Echo “Result:” & vbCrLf & outputLine
End If
‘ 終了コードの確認
If objExec.ExitCode <> 0 Then
WScript.Echo “Command Failed with ExitCode: ” & objExec.ExitCode
End If
—
4. プロダクション環境における「禁じ手」と「鉄則」
現場でバグを生まないための、私からの提言だ。
1. パスの空白対策: `Run` や `Exec` に渡すパスには、必ずダブルクォーテーションをエスケープして含めろ。`” “”” & path & “”” “` のように。これを怠ると、スペースが含まれるフォルダパスで即座に落ちる。
2. パイプラインの限界: `Exec` で大量の出力を受け取る際、`.ReadAll()` が数MBを超えるとメモリがパンクする可能性がある。行単位で読み込むか、一時ファイルにリダイレクトして、VBScript側で `Scripting.FileSystemObject` を使って読み込む設計の方が、遥かに堅牢だ。
3. データベース連携の罠: 外部コマンドの実行結果をDBに書き込む際は、必ず `On Error Resume Next` を活用し、エラー発生時にトランザクションを確実にロールバックせよ。コマンド側が半端な出力をして終了した場合、整合性が崩れる。
結びに代えて
VBScriptは古い。しかし、システムという巨大な機械の歯車を動かすための「潤滑油」として、これほど適した言語はない。
`Run` を使うか `Exec` を使うか、その一瞬の判断にあなたの技術的矜持を込めてほしい。標準出力を制御し、戻り値を監視する。その積み重ねが、誰にも壊せない「堅牢な自動化」を生むのだ。
君のスクリプトが、明日の業務を少しでも楽にすることを祈っている。
