【入門編】【上級プロフェッショナル向け】CScriptの複数非同期起動による大量ファイルFSO並列分割処理ディスパッチャー – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク
スポンサーリンク

限界突破の設計思想:VBScriptによる「大量ファイル並列処理ディスパッチャー」の実装術

こんにちは。自動化の荒野を歩むエンジニアの皆さん。
日々、業務の自動化に励んでいることと思います。マクロの記録から一歩踏み出し、自分でコードを書き始めた皆さんは、今まさに「プログラミングの入り口」に立っています。

しかし、ある時、あなたは「壁」にぶつかるはずです。
「数万個のファイルを処理しようとしたら、スクリプトが終わるまでに昼休みが終わってしまった……」という壁です。

VBScriptは非常に強力で、Windowsのあらゆる操作を操れる素晴らしい言語です。しかし、一つだけ大きな弱点があります。それは「シングルスレッド(単一の作業員)」であること。つまり、一つのスクリプトは、一つの作業を順番にしかこなせません。

今日は、その壁を打ち破るための、伝説的な設計パターン「ディスパッチャー(配分者)モデル」を伝授します。これを知れば、あなたは単なる「スクリプト使い」から、システムを設計する「アーキテクト」へと進化できるはずですよ。

1. なぜ「並列処理」が必要なのか?(概念図)

通常、VBScriptでファイルを処理する場合、以下のような「一列に並んだ行列」になります。

【通常(シングルスレッド)の動き】

`[ファイル1] → [ファイル2] → [ファイル3] → … → [ファイル10000]`
これでは、一つ一つの処理が遅ければ、全体の時間は膨大になります。

【ディスパッチャー(マルチプロセス)の動き】

ここで、「ディスパッチャー(司令塔)」という役割のスクリプトを導入します。司令塔は、仕事を細かく分割し、複数の「ワーカー(作業員)」へ同時に命令を出します。

[ 司令塔 (Dispatcher.vbs) ]
      │
      ├─▶ [ 作業員A (cscript.exe Worker.vbs) ] ──▶ [ ファイル1〜2500 ]
      ├─▶ [ 作業員B (cscript.exe Worker.vbs) ] ──▶ [ ファイル2501〜5000 ]
      ├─▶ [ 作業員C (cscript.exe Worker.vbs) ] ──▶ [ ファイル5001〜7500 ]
      └─▶ [ 作業員D (cscript.exe Worker.vbs) ] ──▶ [ ファイル7501〜10000 ]

このように、OSの機能(`cscript.exe`)を複数立ち上げることで、実質的に「複数の作業員が同時に動いている状態」を作り出すのです。これが並列処理の正体です。

2. 実践:並列処理システムの構築

それでは、実際に動くコードを見ていきましょう。
今回は、「特定のフォルダ内のファイルを、分割して一斉にリネーム(または移動)する」という仕組みを作ります。

プロフェッショナルの知恵として、コマンドライン引数に直接ファイル名を渡すと「文字数制限」に引っかかるため、「処理対象リストを一時ファイルに書き出し、それをワーカーに読み込ませる」という堅牢な設計を採用します。

A. 司令塔:`Dispatcher.vbs`

このスクリプトは、ファイルの一覧を取得し、グループ分けしてワーカーを起動します。

' ===========================================================================
' Dispatcher.vbs
' 役割: ファイルを分割し、複数のWorkerプロセスを起動する司令塔
' ===========================================================================
Option Explicit

Dim fso, shell, targetFolder, workerPath, workerCount
Dim allFiles, fileCount, chunkSize, i, workerIdx
Dim listFile, cmdLine

Set fso = CreateObject("Scripting.FileSystemObject")
Set shell = CreateObject("WScript.Shell")

' --- 設定エリア ---
targetFolder = "C:\Test\TargetFiles" ' 処理したいファイルが入ったフォルダ
workerPath = "C:\Test\Worker.vbs"    ' 呼び出すワーカーのパス
workerCount = 4                      ' 同時に動かす作業員の数(CPUコア数に合わせると効率的)
' ------------------

If Not fso.FolderExists(targetFolder) Then
    WScript.Echo "エラー: 対象フォルダが見つかりません。"
    WScript.Quit
End If

' 1. 全ファイルを取得
Set allFiles = fso.GetFolder(targetFolder).Files
fileCount = allFiles.Count

If fileCount = 0 Then
    WScript.Echo "処理対象のファイルがありません。"
    WScript.Quit
End If

WScript.Echo "総ファイル数: " & fileCount & " 件"
WScript.Echo workerCount & " 個のプロセスで並列処理を開始します..."

' 2. 分割処理の計算
chunkSize = Int(fileCount / workerCount)
If chunkSize = 0 Then chunkSize = fileCount

' 3. ワーカーの起動ループ
For i = 0 To workerCount - 1
    ' 処理範囲の計算
    Dim startIdx, endIdx
    startIdx = (i * chunkSize) + 1
    endIdx = (i + 1) * chunkSize
    If i = workerCount - 1 Then endIdx = fileCount ' 最後のワーカーは端数すべてを担当

    ' 作業員用の「指示書(一時ファイル)」を作成
    listFile = "C:\Test\worker_list_" & i & ".txt"
    Call CreateInstructionList(targetFolder, listFile, startIdx, endIdx)

    ' 4. ワーカーを非同期(バックグラウンド)で起動
    ' Run メソッドの第3引数を False にすることで、終了を待たずに次のループへ進みます(並列化のキモ!)
    cmdLine = "cscript.exe //nologo """ & workerPath & """ """ & listFile & """"
    shell.Run cmdLine, 1, False 
    
    WScript.Echo "Worker " & i & " を起動しました (対象: " & startIdx & "~" & endIdx & ")"
Next

WScript.Echo "すべてのワーカーに指示を出しました。完了を待機してください。"

' --- サブルーチン: 指示書作成 ---
Sub CreateInstructionList(folderPath, outPath, sIdx, eIdx)
    Dim f, currentIdx, fileItem
    Set f = fso.CreateTextFile(outPath, True)
    
    currentIdx = 1
    For Each fileItem In fso.GetFolder(folderPath).Files
        If currentIdx >= sIdx And currentIdx <= eIdx Then
            f.WriteLine fileItem.Path
        End If
        currentIdx = currentIdx + 1
        If currentIdx > eIdx Then Exit For
    Next
    f.Close
End Sub

B. 作業員:`Worker.vbs`

このスクリプトは、司令塔から渡された「指示書」を読み込み、黙々と作業をこなします。

' ===========================================================================
' Worker.vbs
' 役割: 指示書(テキストファイル)を読み込み、実際のファイル操作を行う
' ===========================================================================
Option Explicit

Dim fso, args, listFile, f, filePath
Set fso = CreateObject("Scripting.FileSystemObject")
Set args = WScript.Arguments

' 1. 指示書のパスを受け取る
If args.Count = 0 Then
    WScript.Quit
End If
listFile = args(0)

' 2. 指示書を一行ずつ読み込んで処理
If fso.FileExists(listFile) Then
    Set f = fso.OpenTextFile(listFile, 1) ' 読み取りモード
    
    Do Until f.AtEndOfStream
        filePath = f.ReadLine
        
        If fso.FileExists(filePath) Then
            ' --- ここに実際の処理を書く ---
            ' 例として、ファイル名の先頭に "[PROCESSED_" を付けるリネーム処理
            On Error Resume Next ' エラーが起きても止まらないように(重要!)
            Dim newName
            newName = fso.GetParentFolderName(filePath) & "\PROCESSED_" & fso.GetFileName(filePath)
            fso.MoveFile filePath, newName
            
            If Err.Number <> 0 Then
                ' エラーログの出力(本来はログファイルに書き出すのがプロ)
                ' WScript.Echo "Error processing: " & filePath
                Err.Clear
            End If
            On Error GoTo 0
            ' ----------------------------
        End If
    Loop
    f.Close
    
    ' 3. 終わったら指示書を削除して自分を片付ける
    fso.DeleteFile listFile
End If

3. プロフェッショナルの視点:陥りやすい罠と回避策

ここまで読んで「よし、動かしてみよう!」と思ったあなた。素晴らしい!
しかし、実戦ではいくつかの「落とし穴」が待ち構えています。ここをクリアすれば、あなたはもう初心者ではありません。

① ファイル・ロック(競合)の恐怖

複数のワーカーが「同じファイル」を同時に触ろうとすると、エラーが発生します。

  • 対策: 今回の設計のように、司令塔が「担当範囲」を明確に分けて指示書を作ることで、ワーカー同士が同じファイルに触れないように制御(排他制御)するのが鉄則です。

② コマンドラインの長さ制限

`shell.Run “cscript.exe worker.vbs file1.txt file2.txt …”` のように、ファイル名を直接引数に渡すと、Windowsの仕様で文字数制限(約8191文字)に当たり、途中で切れてしまいます。

  • 対策: 今回の実装で行ったように、「ファイルリストをテキストファイルに書き出し、そのパスだけを渡す」手法が最も安全でプロフェッショナルなやり方です。

③ エラーハンドリングの重要性

並列処理では、一つのワーカーがエラーで止まっても、他のワーカーは走り続けます。

  • 対策: `On Error Resume Next` を適切に使い、エラーが起きても「どのファイルで失敗したか」をログに残す仕組みを組み込みましょう。止まってしまうスクリプトは、自動化において「使い物にならない」とみなされます。

④ リソースの使いすぎ

`workerCount` を 100 とかに設定すれば、さらに速くなる……と思ったら大間違いです。

  • 対策: CPUのコア数やディスクのI/O性能(読み書き速度)を超えてワーカーを増やすと、逆に「作業員同士がぶつかり合って(コンテキストスイッチ)」遅くなります。まずは CPUのコア数と同じか、その2倍程度から試すのがスマートですよ。

まとめ:次の一歩へ

いかがでしたか?
単なる「ファイルの移動」という単純なタスクも、「司令塔」と「作業員」に役割を分けるという視点を持つだけで、圧倒的なパフォーマンスを発揮する「システム」へと昇華されます。

VBScriptは古い言語だと言われることもありますが、その仕組みを深く理解し、こうして設計思想を応用することで、現代の業務にも十分に通用する強力な武器になります。

「動けばいい」から「効率よく、堅牢に動く」へ。
このステップアップを楽しみながら、ぜひ実装してみてくださいね。応援していますよ!

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