【実務・中級編】【一括拡張子変更】サブフォルダも含めた全ファイルを走査し特定拡張子を一元変換する一貫性保持ツール – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

VBScriptで「一括ファイルリネーム」を制す:堅牢な設計と安全なロールバックの極意

業務自動化の現場において、FSO(FileSystemObject)は最も身近な武器だ。しかし、多くのエンジニアはこれを「とりあえず動くコードを書くための道具」としか見ていない。

今回扱う「サブフォルダを含む全ファイルの拡張子一括変換」は、一見単純な再帰処理に見える。だが、「排他制御の欠如」「エラーハンドリングの甘さ」「ロールバック設計の放棄」によって、本番環境で悲劇を招く典型的なツールでもある。

今回は、伝説的アーキテクトの視点から、プロダクション環境で胸を張って実行できる「失敗しないリネームツール」の設計思想を伝授する。

—

1. なぜ「雑なリネーム」が致命傷になるのか

多くの者が書く「再帰関数で`f.Name`を書き換えるだけのコード」には、致命的な欠陥が3つある。

1. アトミック性の欠如: 処理中にエラーが発生した際、どこまで変更したかが不明になる。
2. 重複ファイルの存在: 変換後の名前が既に存在する場合、FSOは例外を投げて停止する。
3. パスの深さ制限: 再帰が深すぎるとスタックオーバーフローやパス長制限(260文字問題)に抵触する。

我々が目指すべきは、「処理の記録(ジャーナル)を残し、失敗した瞬間に即座に原状復帰できる堅牢性」だ。

—

2. 【プロダクションコード】安全性を担保するリネーム・エンジンの構造

このスクリプトは、処理ログを配列に保持し、万が一の異常終了時には即座に逆順処理でロールバックを行う。

‘ VBScript: Robust File Renamer (Transactional Model)
Option Explicit

Dim fso, logFile
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ — 設定エリア —
Dim targetDir, oldExt, newExt
targetDir = “C:\Project\Data”
oldExt = “.xml.tmp”
newExt = “.xml”
‘ ——————

‘ 変更履歴を記録するスタック(ロールバック用)
Dim undoStack()
ReDim undoStack(0)

Sub Main()
On Error Resume Next
Call ProcessFolder(fso.GetFolder(targetDir))

If Err.Number <> 0 Then
WScript.Echo “致命的なエラーを検知しました。ロールバックを開始します…”
Call RollbackChanges()
Else
WScript.Echo “全ての処理が正常終了しました。”
End If
End Sub

Sub ProcessFolder(folder)
Dim f, subFolder, newName

‘ ファイル走査
For Each f In folder.Files
If LCase(fso.GetExtensionName(f.Name)) = “tmp” Then ‘ 簡易判定
If Right(f.Name, Len(oldExt)) = oldExt Then
newName = Left(f.Path, Len(f.Path) – Len(oldExt)) & newExt

‘ 変更履歴を記録してからリネーム
ReDim Preserve undoStack(UBound(undoStack) + 1)
undoStack(UBound(undoStack)) = f.Path & “|” & newName

f.Name = fso.GetFileName(newName)
End If
End If
Next

‘ サブフォルダを再帰的に走査
For Each subFolder In folder.SubFolders
Call ProcessFolder(subFolder)
Next
End Sub

Sub RollbackChanges()
Dim i, parts
‘ 逆順に復元
For i = UBound(undoStack) To 1 Step -1
parts = Split(undoStack(i), “|”)
If fso.FileExists(parts(1)) Then
fso.GetFile(parts(1)).Name = fso.GetFileName(parts(0))
End If
Next
End Sub

Call Main()

—

3. 設計の勘所:なぜこの書き方なのか

A. `On Error Resume Next` の正しい使い方

VBScriptには強力な例外処理機構がない。そのため、`On Error Resume Next` を戦略的に使い、エラーの直後に必ず `If Err.Number <> 0` で判定する。これを怠るスクリプトは、動いているフリをして何もしていない「サイレント・フェイラー」を量産する。

B. ロールバック設計(Undo Stack)

リネームという「不可逆的な破壊行動」を行う以上、変更前のパスと変更後のパスのペアをメモリ上の配列(スタック)に保持するのは必須だ。これにより、スクリプトが途中で落ちても、手動でファイルを一個ずつ戻すという惨事を回避できる。

C. パフォーマンスの最適化

FSOの `GetFolder` や `Files` コレクションへのアクセスはコストが高い。可能な限り、ループ内でのオブジェクト生成を抑え、文字列操作で完結させる設計にしている。大規模な階層を扱う場合は、再帰呼び出しの深さに注意を払うこと(スタック溢れを避けるため、深さが予測困難な場合はスタックデータ構造を自前で実装するのがベターだ)。

—

4. アーキテクトからの提言

このツールを導入する際、「ログ出力」を甘く見てはいけない。
今回はメモリ上の配列でロールバックを実現しているが、数万件を超えるファイルを扱う場合は、配列ではなく外部のテキストファイル(ログ)に `[変更前パス] -> [変更後パス]` を書き出し、そこから逆変換するロジックに切り替えるべきだ。

自動化とは「楽をすること」ではない。「人間が介在するとミスが起きるプロセスを、計算可能な論理に落とし込むこと」である。

君たちが書くその一行のコードが、次に触れる誰かの業務を救うか、あるいは新たなバグの温床となるか。常に「失敗した場合の復旧」を設計の中心に据えてほしい。それが、プロフェッショナルとしての最低限の流儀だ。

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