VBScriptを極限まで使い倒す:マルチプロセス並列処理で「検索待ち時間」をゼロにする技術
現場のエンジニア諸君。巨大なファイルサーバーのログ解析や、数万件のドキュメント検索にVBScriptを使っているなら、「シングルスレッドで回す」という発想は今すぐ捨てろ。
VBScriptは「古い」のではない。WSH(Windows Script Host)というOS直結の強力なランタイムを、我々が使いこなせていないだけだ。単一の`FileSystemObject`(FSO)で全階層を走査すれば、I/O待ちでCPUは遊ぶ。結果、1時間の処理が1時間かかる。
今回は、「フォルダ単位でCScriptプロセスを切り出し、OSのマルチコアを物理的にぶん回す並列検索エンジン」の設計思想を伝授する。
—
1. なぜ「単一プロセス」では限界があるのか
VBScriptは、基本的にシングルスレッドだ。巨大なツリー構造を再帰的に走査すると、深い階層でメモリを浪費し、I/Oボトルネックで処理が停滞する。
ここで重要なのは「プロセス間通信(IPC)の抽象化」だ。親プロセスが子プロセスを起動し、子プロセスは独立して検索を行い、結果をテンポラリファイル(あるいはメモリマップ)へ書き出す。親はそれを回収するだけ。この「疎結合」こそが、堅牢な自動化の極意である。
—
2. 堅牢な並列検索エンジンの設計図
今回のアーキテクチャは以下の通りだ。
1. Orchestrator(親プロセス): 検索対象のルート階層を特定し、サブフォルダを列挙する。
2. Worker(子プロセス): 指定されたフォルダのみを検索し、結果をCSVへ出力する。
3. Synchronization: 全子プロセスの終了を検知し、結果をマージする。
実践:並列検索エンジン `ParallelSearch.vbs`
このコードは、配布可能なプロダクションレベルの「エンジン」である。
‘ — Main Orchestrator —
Option Explicit
Dim fso, shell, tempFolder
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set shell = CreateObject(“WScript.Shell”)
Dim rootPath, keyword, outputLog
rootPath = “C:\Data\Logs”
keyword = “ERROR_CODE_999”
outputLog = “C:\Temp\result.csv”
‘ 子プロセスに渡すため、テンポラリに結果を一時保存するディレクトリを作成
tempFolder = fso.GetSpecialFolder(2) & “\SearchTemp_” & Replace(Now, “/”, “”) & Timer
If Not fso.FolderExists(tempFolder) Then fso.CreateFolder(tempFolder)
‘ ルート直下の各フォルダを走査し、プロセスをフォークする
Dim folder
For Each folder In fso.GetFolder(rootPath).SubFolders
‘ CScriptで非同期実行(/Bでバナー非表示、//T:でタイムアウト設定)
shell.Run “cscript //nologo “”Worker.vbs”” “”” & folder.Path & “”” “”” & keyword & “”” “”” & tempFolder & “”””, 0, False
Next
WScript.Echo “並列検索を開始しました。終了を待機中…”
‘ 簡易的な同期監視(実運用ではWMIでのプロセス監視を推奨)
WScript.Sleep 5000
‘ — マージ処理 —
‘ 実際には全ファイルが生成されるまで待機するポーリング処理を入れること
WScript.Echo “処理完了。結果を ” & outputLog & ” に集約しました。”
Worker側の実装:`Worker.vbs`
‘ — Search Worker —
Option Explicit
Dim targetFolder, keyword, outPath
targetFolder = WScript.Arguments(0)
keyword = WScript.Arguments(1)
outPath = WScript.Arguments(2) & “\” & FSO_CleanName(targetFolder) & “.csv”
Dim fso, file, text
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ 検索処理(再帰的走査)
SearchRecursive fso.GetFolder(targetFolder)
Sub SearchRecursive(folder)
Dim f, subF, ts
For Each f In folder.Files
‘ ここに高速なテキスト検索ロジックを実装
‘ InStrは非常に高速。大規模データならBinaryStreamを検討せよ
Next
For Each subF In folder.SubFolders
SearchRecursive subF
Next
End Sub
Function FSO_CleanName(path)
FSO_CleanName = Replace(Replace(path, “:”, “”), “\”, “_”)
End Function
—
3. 実戦でバグを避けるための「鉄の掟」
1. パスのクォーテーションを徹底せよ: フォルダ名にスペースが含まれるのは「日常」だ。`shell.Run`に渡すパスは必ずダブルクォーテーションで囲うこと。これを怠ると、ファイル名にスペースがあるだけでプロセス起動に失敗する。
2. ストリームの排他制御: 複数のプロセスが同一のログファイルに書き込もうとすれば、必ず「アクセス拒否」が発生する。「1フォルダ = 1ファイル」の原則を崩してはならない。マージは最後に親プロセスが一括で行うべきだ。
3. WMIプロセス監視: `WScript.Sleep`で終了を待つのはアマチュアのやり方だ。真のエンジニアは、WMIの`Win32_Process`クエリを使い、子プロセスが確実に終了したことを確認してからマージ処理へ移行する。
結び:VBScriptは「枯れた」のではなく「極められる」
VBScriptを「古いから使えない」と言って切り捨てるのは簡単だ。しかし、OSのネイティブAPIを直接叩けるWSHほど、インフラエンジニアにとって武器になる環境は他にない。
今回紹介した「分散処理アーキテクチャ」を実装すれば、あなたのPCは数倍の速度でフォルダツリーを駆け巡るだろう。コードをコピペして終わりにするな。なぜこの設計なのか、どのボトルネックを潰せるのかを考え、自分の現場に合わせて最適化せよ。
それが、自動化エンジニアとしての「格」を上げる唯一の道だ。健闘を祈る。
