【実務・中級編】【上級プロフェッショナル向け】FSO一括変更・削除実行前に「全変更対象メタデータ」のSnapshot(JSON/CSV)を保持し一括復元(Undo)を可能にする安全機構 – VBScript (Visual Basic Scripting Edition)解析バイブル

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

破壊的変更を「制御可能なイベント」に変える:FSOにおけるSnapshot & Undoエンジンの設計論

自動化エンジニアとして現場に立つとき、我々が最も恐れるべきは何のバグでもない。「取り返しのつかない破壊的操作」だ。

大量のファイルを一括でリネームする、あるいはディレクトリ構造を再編する。こうしたスクリプトを「動くから」という理由だけで、事前の状態保存(Snapshot)なしにデプロイする者は、エンジニアではなく単なる「スクリプタ」に過ぎない。

プロフェッショナルが作るツールには、必ず「退路(Undo)」が用意されている。本稿では、VBScriptのFileSystemObject (FSO) を駆使し、操作前のメタデータを完全に記録し、万が一の誤操作時に一瞬で元の状態へ復元する「Snapshot & Undoエンジン」の設計と実装について、その極限の知見を伝授する。

1. なぜ「単なるループ処理」では足りないのか

一般的な自動化スクリプトは、以下のような構造になりがちだ。
1. 対象フォルダを走査する
2. 条件に合致するファイルをリネーム/移動する

この設計の致命的な欠陥は、「操作の原子性(Atomicity)」が欠如している点にある。
途中で権限エラーが発生した、あるいは正規表現のロジックが僅かに間違っていた場合、ファイル群は「半分リネームされた中途半端な状態」で放置される。この状態からの復旧は、もはや手作業による地獄の作業となる。

我々が目指すべきは、「状態の記録(Snapshot)」と「実行(Execution)」を完全に分離した設計である。

設計の三原則

1. Metadata First: 操作を開始する前に、対象ファイルの「現時点の真実(Path, Size, Timestamp, Name)」を外部ファイルに書き出す。
2. Idempotency (冪等性) の意識: Undo操作自体が失敗しても、二度実行しても状態を壊さない設計にする。
3. Single Source of Truth: ログファイルこそが、システムの状態を示す唯一の正解とする。

2. アーキテクチャ:Snapshot & Undo Engine

設計図はシンプルだが、堅牢である必要がある。

1. Phase 1: Snapshot Generation
対象となる全ファイルのメタデータを抽出し、CSV形式(または構造化されたテキスト)で保存する。この際、「変更後のパス」もあらかじめ計算して含めておくことが肝要だ。
2. Phase 2: Execution
Snapshotに基づいて実際のFSO操作を行う。
3. Phase 3: Rollback (Undo)
Snapshotファイルを読み込み、`NewPath` を `OldPath` に、`OldPath` を `NewPath` に入れ替える逆転操作を実行する。

3. プロダクション・コード:FileActionEngine

以下に、クラスベースでカプセル化された、実戦投入可能なコード例を示す。VBScriptにおいて、変数のスコープとエラーハンドリングを制御するため、クラス設計は必須である。

' =============================================================================
' FileActionEngine Class
' 目的: ファイル操作のSnapshot作成、実行、およびUndo機能の提供
' 設計思想: 破壊的操作の前に必ずメタデータを記録し、復元可能性を担保する
' =============================================================================
Class FileActionEngine
    Private FSO
    Private SnapshotFile
    Private Delimiter

    ' コンストラクタ
    Private Sub Class_Initialize()
        Set FSO = CreateObject("Scripting.FileSystemObject")
        Delimiter = "|" ' CSVより安全な区切り文字を選択
        SnapshotFile = ""
    End Sub

    ' --- メソッド: Snapshotの作成 ---
    ' 対象フォルダ内のファイルを走査し、変更計画をSnapshotとして保存する
    Public Function CreateSnapshot(targetFolder, renameRuleFunc)
        ' 注: VBScriptでは関数を引数に渡せないため、
        ' 本来はロジックをクラス内メソッドとして定義するか、
        ' 外部から定義されたルールに基づいて処理を行う必要がある。
        ' ここでは簡略化のため、直接的なパス操作ロジックを想定。
        
        On Error Resume Next
        Dim ts, folder, file, oldPath, newPath, metaLine
        Set folder = FSO.GetFolder(targetFolder)
        
        ' Snapshotファイルの生成 (実行ディレクトリにタイムスタンプ付きで作成)
        SnapshotFile = "snapshot_" & Replace(Replace(Now, "/", ""), ":", "") & ".log"
        Set ts = FSO.CreateTextFile(SnapshotFile, True)

        For Each file In folder.Files
            oldPath = file.Path
            
            ' 【重要】ここで「変更後のパス」を計算する
            ' 実務ではここを高度な正規表現ロジックに置き換える
            newPath = Replace(oldPath, "old_", "new_") 

            ' メタデータの記録: 旧パス|新パス|サイズ|更新日時
            ' これにより、Undo時に「何がどう変わるべきか」が完全にわかる
            metaLine = oldPath & Delimiter & newPath & Delimiter & _
                       file.Size & Delimiter & file.DateLastModified
            ts.WriteLine metaLine
        Next

        ts.Close
        
        If Err.Number <> 0 Then
            MsgBox "Snapshot作成失敗: " & Err.Description, vbCritical
            CreateSnapshot = False
        Else
            MsgBox "Snapshot作成完了: " & SnapshotFile & vbCrLf & "操作を開始できます。", vbInformation
            CreateSnapshot = True
        End If
        On Error GoTo 0
    End Function

    ' --- メソッド: 変更の実行 ---
    ' Snapshotに基づいて実際にファイル名を変更する
    Public Sub Execute()
        If SnapshotFile = "" Then Exit Sub
        
        Dim ts, line, parts, oldP, newP
        Set ts = FSO.OpenTextFile(SnapshotFile, 1) ' Read mode

        Do Until ts.AtEndOfStream
            line = ts.ReadLine
            parts = Split(line, Delimiter)
            If UBound(parts) >= 1 Then
                oldP = parts(0)
                newP = parts(1)

                ' 実行前に存在確認を行う堅牢な設計
                If FSO.FileExists(oldP) Then
                    ' 変更先が既に存在する場合はエラーを回避
                    If Not FSO.FileExists(newP) Then
                        FSO.MoveFile oldP, newP
                    Else
                        ' ログにスキップを記録(実務ではログファイルへの書き出しが望ましい)
                    End If
                End If
            End If
        Loop
        ts.Close
        MsgBox "一括変更処理が完了しました。", vbInformation
    End Sub

    ' --- メソッド: Undo (一括復元) ---
    ' Snapshotを逆引きして、元の状態に戻す
    Public Sub Undo()
        If SnapshotFile = "" Or Not FSO.FileExists(SnapshotFile) Then
            MsgBox "有効なSnapshotファイルが見つかりません。", vbExclamation
            Exit Sub
        End If

        Dim ts, line, parts, oldP, newP
        Set ts = FSO.OpenTextFile(SnapshotFile, 1)

        Do Until ts.AtEndOfStream
            line = ts.ReadLine
            parts = Split(line, Delimiter)
            If UBound(parts) >= 1 Then
                oldP = parts(0)
                newP = parts(1)

                ' 【逆転ロジック】新パス(newP)から旧パス(oldP)へ戻す
                If FSO.FileExists(newP) Then
                    If Not FSO.FileExists(oldP) Then
                        FSO.MoveFile newP, oldP
                    End If
                End If
            End If
        Loop
        ts.Close
        MsgBox "Undo操作が完了しました。元の状態を確認してください。", vbInformation
    End Sub
End Class

' =============================================================================
' メインルーチン (Driver)
' =============================================================================
Dim engine
Set engine = New FileActionEngine

' 1. Snapshotの作成
If engine.CreateSnapshot("C:\TestFolder") Then
    ' 2. ユーザーの確認を待つ(ここがプロの作法)
    Dim ans
    ans = MsgBox("変更を実行しますか?", vbYesNo + vbQuestion, "実行確認")
    
    If ans = vbYes Then
        engine.Execute
        
        ' 3. 失敗した場合のUndoテスト(実務ではエラーハンドリング内で呼ぶ)
        Dim undoAns
        undoAns = MsgBox("Undo(元に戻す)を実行しますか?", vbYesNo + vbQuestion, "復元確認")
        If undoAns = vbYes Then
            engine.Undo
        End If
    End If
End If

4. 現場で陥る「落とし穴」と回避策

このエンジンを運用する上で、以下の3点には細心の注意を払え。

① ファイルロックの競合

FSOの `MoveFile` は、対象ファイルがExcelや他のプロセスによって開かれている場合、即座に実行時エラーを吐く。
対策: `Execute` メソッド内で `On Error Resume Next` を適切に使い、失敗した行を「エラーログ」としてSnapshotとは別に記録する設計にせよ。全てのファイルが成功したか、一部失敗したのかを明確に区別できなければ、Undoの整合性が崩れる。

② パス長制限 (MAX_PATH)

Windowsの伝統的な制限(260文字)により、深い階層でのリネームは失敗する。
対策: 非常に深いディレクトリを扱う場合は、パスの先頭に `\\?\` プレフィックスを検討するか、対象範囲を限定する運用ルールを徹底せよ。

③ Snapshotの整合性破壊

Snapshotファイル自体が改ざんされたり、削除されたりした場合、Undoは不可能になる。
対策: Snapshotファイルは「実行用の一時データ」として扱うが、重要な業務プロセスにおいては、Snapshotファイルを読み取り専用属性 (`File.Attributes = 1`) に設定し、保護をかけるのがプロの振る舞いだ。

5. 結論:エンジニアとしての矜持

「動けばいい」という思考は、自動化の世界では「いつか必ず破綻する」と同義である。

優れた自動化エンジニアとは、コードの行数が多い者でも、複雑なアルゴリズムを駆使する者でもない。「失敗したときに、いかに安全に、いかに速やかに、いかに確実に元に戻せるか」を、設計の第一段階から考慮できる者のことだ。

このSnapshot & Undoパターンをマスターし、あなたのツールに「責任」という名の堅牢性を宿らせてほしい。

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