【一括リネーム】FileSystemObjectによる安全かつ高速なファイル名一括変換ツールの設計と実装
レガシーシステムの維持保全、あるいはローカル環境でのバッチ処理において、VBScript(Visual Basic Scripting Edition)はその即時性と軽量性において今なお代替不可能な価値を持つ。コンパイルのオーバーヘッドを必要とせず、`WScript.Shell`や`Scripting.FileSystemObject`(以下、FSO)を駆使することで、OSの深部へとダイレクトにアプローチできるからだ。
本稿では、現場で頻発する「大量の画像や帳票PDFに対し、タイムスタンプや連番を付与して一括リネームする」という要件を題材に、単なるスクリプトの域を超えた、実運用に耐えうる堅牢性とパフォーマンスを兼ね備えた自動化ツールの設計思想と実装を解説する。
—
1. アーキテクチャ設計の要点:なぜFSOとエラーハンドリングの極限化が必要か
ファイルシステムを操作するスクリプトにおいて、最大の敵は「予期せぬI/Oエラー」と「リソースリーク」である。特に一括リネーム処理では、途中でアクセス権限エラーやファイル名重複(コリジョン)が発生した際、処理が途中でアボートし、中途半端な状態(半改名状態)でファイルシステムが放置されるリスクが常に伴う。
シニアエンジニアが担保すべき要件は以下の3点に集約される。
1. トランザクション的思考の導入: リネーム対象の事前スキャンと、実行時の厳密なエラーハンドリング。
2. オブジェクトのライフサイクル管理: FSOやシェルオブジェクトの不必要な生成・破棄を避け、メモリフットプリントを最小化する。
3. 文字コードとロケールの安全性: 2バイト文字(日本語ファイル名)を含むパスを安全に処理するための内部エンコーディングの意識。
—
2. 実装コード:安全な一括リネームエンジン
以下のコードは、指定したフォルダ内の特定拡張子を持つファイルに対し、ISO 8601ベースの日付プレフィックス(`YYYYMMDD_HHMMSS`)およびゼロパディングされた連番を付与し、安全にリネームを実行するVBScriptの実装である。
‘ ==============================================================================
‘ Script Name: SecureBatchRenamer.vbs
‘ Description: FileSystemObjectを用いた安全なファイル一括リネームツール
‘ Author: Chief Architect
‘ ==============================================================================
Option Explicit
‘ 定数定義
Const TargetDir = “C:\Reports\TargetFolder” ‘ 対象ディレクトリ
Const TargetExt = “pdf” ‘ 対象拡張子(ドット含まず)
Const AddTimestamp = True ‘ タイムスタンプを付与するか
Const AddSequence = True ‘ 連番を付与するか
‘ メイン処理の実行
Call Main()
Sub Main()
Dim fso, targetFolder, fileList, fileItem
Dim baseTimestamp, seqCounter
Dim oldPath, newPath, ext, baseName
Dim successCount, errorCount
‘ 実行開始時間の取得(タイムスタンプ用)
baseTimestamp = GetFormattedDateTime(Now)
seqCounter = 1
successCount = 0
errorCount = 0
‘ FSOのインスタンス化(スクリプトスコープで単一保持)
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ フォルダ存在確認
If Not fso.FolderExists(TargetDir) Then
WScript.Echo “[FATAL] 指定されたフォルダが存在しません: ” & TargetDir
Set fso = Nothing
WScript.Quit 1
End If
Set targetFolder = fso.GetFolder(TargetDir)
Set fileList = targetFolder.Files
WScript.Echo “=== 一括リネーム処理を開始します ===”
WScript.Echo “対象パス: ” & TargetDir
WScript.Echo “————————————————–”
‘ エラーハンドリングの有効化(個別ファイルのエラーでスクリプト全体を止めない)
On Error Resume Next
For Each fileItem In fileList
‘ 拡張子の判定(大文字小文字を区別しない比較)
ext = LCase(fso.GetExtensionName(fileItem.Name))
If ext = LCase(TargetExt) Then
‘ 新しいファイル名の組み立て
‘ 構成: [タイムスタンプ]_[連番]_[元のファイル名].[拡張子]
baseName = fso.GetBaseName(fileItem.Name)
Dim prefix
prefix = “”
If AddTimestamp Then
prefix = prefix & baseTimestamp & “_”
End If
If AddSequence Then
‘ 4桁のゼロパディング連番
prefix = prefix & Right(“0000” & CStr(seqCounter), 4) & “_”
End If
‘ 最終的なパスの構築
oldPath = fileItem.Path
newPath = targetFolder.Path & “\” & prefix & baseName & “.” & ext
‘ 同名ファイルが存在しない場合の安全なリネーム
If Not fso.FileExists(newPath) Then
‘ FSOのMoveFileメソッドを使用(同一ボリューム内では実質リネーム)
fso.MoveFile oldPath, newPath
If Err.Number = 0 Then
WScript.Echo “[SUCCESS] ” & fileItem.Name & ” -> ” & fso.GetFileName(newPath)
successCount = successCount + 1
seqCounter = seqCounter + 1
Else
WScript.Echo “[ERROR] リネーム失敗 (” & fileItem.Name & “): ” & Err.Description
Err.Clear
errorCount = errorCount + 1
End If
Else
WScript.Echo “[WARNING] 既に同名のファイルが存在するためスキップします: ” & fso.GetFileName(newPath)
errorCount = errorCount + 1
End If
End If
Next
‘ エラーハンドリングの復元
On Error GoTo 0
WScript.Echo “————————————————–”
WScript.Echo “=== 処理完了 ===”
WScript.Echo “成功件数: ” & successCount & ” 件”
WScript.Echo “失敗/スキップ件数: ” & errorCount & ” 件”
‘ オブジェクトの明示的解放(メモリ最適化)
Set fileList = Nothing
Set targetFolder = Nothing
Set fso = Nothing
End Sub
‘ ——————————————————————————
‘ 補助関数: 日時をファイル名安全な文字列 (YYYYMMDD_HHMMSS) に変換
‘ ——————————————————————————
Function GetFormattedDateTime(targetDate)
Dim yyyy, mm, dd, hh, nn, ss
yyyy = CStr(Year(targetDate))
mm = Right(“00” & CStr(Month(targetDate)), 2)
dd = Right(“00” & CStr(Day(targetDate)), 2)
hh = Right(“00” & CStr(Hour(targetDate)), 2)
nn = Right(“00” & CStr(Minute(targetDate)), 2)
ss = Right(“00” & CStr(Second(targetDate)), 2)
GetFormattedDateTime = yyyy & mm & dd & “_” & hh & nn & ss
End Function
—
3. チーフアーキテクトが解説するコードの急所
オブジェクトのライフサイクルとメモリ管理
VBScriptのCOMコンポーネント(`Scripting.FileSystemObject`など)は、スクリプトの終了時に自動解放される仕様になっている。しかし、数万件規模のファイルを走査するバッチ処理や、常駐型プロセスから呼び出されるモジュールにおいて、オブジェクトの参照を放置することはメモリリーク(特にCOMラッパーのメモリ断片化)の原因となる。
処理の終端で `Set fso = Nothing` を明示的に記述し、VBScriptエンジンにガベージコレクションの契機を与えることは、プロフェッショナルとしての最低限の作法である。
同一ボリューム内における `MoveFile` の特性
FSOの `MoveFile` メソッドは、名前の通りファイルの移動を行うものだが、移動元と移動先が同一ボリューム(同一ドライブ)上にある場合、ファイル実データの物理コピーは行われず、MFT(Master File Table)のポインタ書き換え(メタデータの変更)のみで処理される。
これにより、大容量のPDFや高解像度画像が数千個存在してであっても、処理は一瞬で完了する。このOSレベルの挙動を理解しているかどうかが、パフォーマンスに直結する。
局所的なエラーハンドリング (`On Error Resume Next`)
一括処理において、1つのファイルが別プロセス(プレビュー表示中、ネットワークの遅延など)によってロックされているためにエラーが発生することは日常茶飯事である。
全体を `On Error Resume Next` で囲みつつ、ループ内で `Err.Number` を監視・クリアすることで、「エラーが発生した個体を安全にスキップし、残りのファイル群の処理を完遂する」という高い可用性を実現している。
—
4. レガシー環境・システム間連携における運用知見
1. 文字コードの罠 (ANSI vs UTF-8)
VBScriptファイル自体をUTF-8(BOM付き)で保存した場合、環境によっては日本語コメントやメッセージ出力が文字化けする。社内ニッチな環境で運用する場合は、あえてShift-JIS (CP932)でエンコーディングして保存するか、WScriptの実行ホスト(`cscript.exe`)のコードページを意識することが不可欠である。
2. タスクスケジューラからのサイレント実行
GUIを持たないサーバ環境で本スクリプトを定期実行する場合、`WScript.Echo` は無視されるかエラーの原因になる。ログ出力が必要な場合は、FSOの `OpenTextFile` を用いてテキストログへリダイレクトする設計へ拡張すべきである。
VBScriptは「古い言語」として片付けられがちだが、OSのプリミティブな機能を最小限のコードで叩けるこの特性は、現代のクラウドネイティブなコンテナ環境におけるサイドカー的スクリプトとしても、依然として強力な武器となる。本稿で示した設計思想をベースに、各現場の要件に合わせた堅牢な自動化基盤を構築してほしい。
