脆弱なスクリプトを卒業せよ:VBScriptにおける「階層型フォルダ自動生成」の極限設計
業務自動化の現場において、プロフェッショナルとアマチュアを分かつ境界線はどこにあるか?
それは「正常系」のコードを書けるかどうかではない。「想定外の環境変化」に対して、いかに堅牢(ロバスト)な設計を組み込めるか、だ。
VBScriptを用いたファイル操作において、初心者が必ずと言っていいほど陥る罠がある。それは、`FileSystemObject`(以下FSO)の`CreateFolder`メソッドを、あたかもLinuxの`mkdir -p`のように万能なものだと誤解することだ。
本記事では、FSOの仕様による「階層欠落エラー」を完全に封じ込め、どのような深い階層指定が来ても、あるいは親ディレクトリが存在しない環境であっても、確実に、かつ安全にディレクトリ構造を構築する「プロフェッショナル仕様の関数」を伝授する。
1. なぜあなたのコードは、本番環境で死ぬのか
まず、問題の本質を理解せよ。FSOの`CreateFolder`メソッドには、極めて単純で、かつ実務においては致命的な制約がある。
「指定したパスの直前の親フォルダが存在していなければ、即座に実行時エラーを吐いて停止する」
例えば、`C:\Work\ProjectA\Logs` というパスを作りたいとする。
もし `C:\Work` までは存在するが、`ProjectA` が存在しない場合、`CreateFolder(“C:\Work\ProjectA\Logs”)` を実行した瞬間にスクリプトはクラッシュする。
多くの初学者は、ここで「フォルダが存在するかチェックしてから作る」という回避策を考える。しかし、それだけでは不十分だ。なぜなら、「作るべき階層が複数段にわたって欠落している場合」、チェックと作成を繰り返すロジックを自前で書かなければならないからだ。
これを解決するには、パスを最小単位(セグメント)に分解し、ルートから順に「存在確認」と「作成」を積み上げていく「逐次構築アプローチ」が必要不可欠となる。
2. プロダクションクオリティの設計指針
単に「動く」関数を作るのではない。保守性とパフォーマンスを両立させた、現場で通用する設計指針を以下に定義する。
1. パスの正規化: 末尾のバックスラッシュ(`\`)の有無による挙動の揺らぎを排除する。
2. 再帰的ではなく反復的(Iterative)なアプローチ: VBScriptにおける再帰呼び出しは、スタックオーバーフローのリスクを孕む。ループによる階層走査が最も安全だ。
3. オブジェクトのライフサイクル管理: ループ内で毎回 `FileSystemObject` を生成するのは、リソースの無駄遣いだ。オブジェクトは一度だけ生成し、使い回せ。
4. エラーハンドリングの局所化: `On Error Resume Next` をスクリプト全体に撒き散らすな。エラーの検知は、最小限の範囲で行い、即座に制御を戻すべきだ。
3. 実装例:多段サブフォルダ自動安全生成関数
以下に、これらすべての設計思想を詰め込んだ、そのままプロダクション環境に投入可能なコードを示す。
' ==============================================================================
' Module: FileSystemAutomation
' Description: 階層的なフォルダ作成を安全に行うための堅牢な関数群
' Author: Lead Architect
' ==============================================================================
Option Explicit
' テスト実行用メインルーチン
Call Main()
Sub Main()
Dim fso, targetPath
Set fso = CreateObject("Scripting.FileSystemObject")
' 【テストケース】存在しない深い階層を指定
' ※環境に合わせて適宜書き換えて実行せよ
targetPath = "C:\Temp\AutomationTest\Project_Alpha\Logs\Daily"
On Error Resume Next
Call SafeCreateFolder(fso, targetPath)
If Err.Number <> 0 Then
MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical, "Error"
Else
MsgBox "フォルダ構造の構築に成功しました: " & vbCrLf & targetPath, vbInformation, "Success"
End If
On Error GoTo 0
Set fso = Nothing
End Sub
' ------------------------------------------------------------------------------
' Function: SafeCreateFolder
' Purpose: 指定されたパスを解析し、欠落している中間ディレクトリを含めて全て作成する
' Arguments:
objFSO (FileSystemObject): 使用するFSOオブジェクト
strPath (String): 作成したい完全パス
' ------------------------------------------------------------------------------
Sub SafeCreateFolder(ByRef objFSO, ByVal strPath)
Dim arrParts, strCurrentPath, i
' 1. パスの正規化 (末尾の \ を除去して分割の狂いを防ぐ)
If Right(strPath, 1) = "\" Then
strPath = Left(strPath, Len(strPath) - 1)
End If
' 2. パスを区切り文字 "\" で分解
arrParts = Split(strPath, "\")
' 3. ルート(ドライブレター)の構築
' arrParts(0) は "C:" のようになる
strCurrentPath = arrParts(0)
' 4. 階層を一つずつ積み上げる (逐次構築)
' i = 1 から開始することで、ドライブレターの直後から処理を行う
For i = 1 To UBound(arrParts)
' 現在の階層までのパスを組み立て
strCurrentPath = strCurrentPath & "\" & arrParts(i)
' 5. 存在確認と作成
' ここで On Error Resume Next を使うのは「存在確認の失敗」を許容するためではなく、
' 「作成の試行」における制御フローのためである
If Not objFSO.FolderExists(strCurrentPath) Then
On Error Resume Next
objFSO.CreateFolder(strCurrentPath)
If Err.Number <> 0 Then
' 権限不足やドライブが存在しない等の致命的なエラーを検知
Err.Raise Err.Number, "SafeCreateFolder", "フォルダ作成に失敗しました: " & strCurrentPath & " (" & Err.Description & ")"
End If
On Error GoTo 0
End If
Next
End Sub
4. アーキテクトの視点:さらに踏み込んだ注意点
この関数を実務で使用するにあたり、以下の「現場のリアル」を忘れてはならない。
権限とネットワークパス(UNC)
この関数は、ローカルドライブだけでなく、`\\Server\Share\Folder` といったUNCパスでも動作するように設計されている。しかし、ネットワーク越しの場合、中間ディレクトリの作成権限がサーバー側で制限されているケースが多々ある。エラーハンドリングにおいて、単に「失敗した」と出すのではなく、`Err.Description` を必ずログに記録する仕組みを併設せよ。
データベースやCSV連携時の原子性(Atomicity)
もし、このフォルダ作成が「データベースへのレコード登録」や「CSVファイルへの書き出し」の直前に行われる場合、フォルダ作成に失敗したのにDB処理だけが進んでしまうという不整合が起きる。
「フォルダ作成」→「ファイル作成」→「DB更新」という順序を厳守し、どこか一つでも失敗したら、それまでの処理をロールバック(あるいは異常終了)させる設計思想を持て。
パフォーマンスの重み
`FileSystemObject` はCOMオブジェクトである。`Split` や `FolderExists` を呼び出すたびに、プロセス間通信に近いオーバーヘッドが発生する。数千個のフォルダをループ内で作成するような極限のパフォーマンスが求められる場面では、FSOを使い回すだけでなく、OSのシェルコマンド(`mkdir`)を `WshShell.Run` で叩く方が高速な場合もある。しかし、「堅牢性」を優先するなら、今回提示したFSOによる逐次構築が正解だ。
結論
「動けばいい」というコードは、技術負債を生む。
「どのような環境でも、どのような階層でも、正しく振る舞う」コードこそが、真に業務を自動化し、エンジニアを解放する。
今回紹介した `SafeCreateFolder` 関数を、君たちのプロジェクトの「標準ライブラリ」に組み込むことを強く推奨する。
