FSOにおける「ノイズ」を排除せよ:堅牢なファイル走査を実現するフィルタリング・アーキテクチャ
業務自動化の世界において、スクリプトの成否を分けるのは「正常系をいかに速く処理するか」ではない。「想定外の異常系(ノイズ)をいかにエレガントに無視するか」である。
現場のエンジニアが書くFSO(FileSystemObject)のコードは、往々にしてナイーブ(無垢)すぎる。フォルダ内のファイルを単純に `For Each` で回し、全てのファイルに対して処理を試みる。その結果、Officeが生成した一時ファイル(`~$`で始まるファイル)や、システムがロックしている `.tmp` ファイルに衝突し、実行時エラーでプロセスが停止する。これは「動くスクリプト」ではあっても、「プロダクション品質のツール」ではない。
本稿では、中級者への階段を登るために不可欠な、「フィルタリング・ファースト」の設計思想と、実務で即戦力となる堅牢な実装パターンを伝授する。
1. なぜ、あなたのスクリプトは「突然」死ぬのか
FSOを用いたファイル走査において、エンジニアを苦しめる「ノイズ」の正体は主に3つある。
① Officeの隠し一時ファイル(Lock Files)
ExcelやWordを開いている最中、同じディレクトリには `~$Book1.xlsx` のような隠しファイルが生成される。これらは実体のあるデータではなく、編集中のロック状態を示すメタデータに近い。これに対して「ファイルを開く」「内容を読み取る」といった操作を仕掛ければ、アクセス拒否(Permission Denied)の例外が飛んでくる。
② システム・テンポラリファイル(.tmp / .log)
アプリケーションが書き込み途中の `.tmp` ファイルや、ログファイル。これらはプロセスによって常にハンドル(File Handle)が掴まれており、読み取りや移動の操作に対して極めて脆弱だ。
③ 属性によるアクセス制限(Hidden / System Attributes)
ファイルが存在していても、その属性が `Hidden` や `System` である場合、通常のアクセス権限ではエラーを誘発する。
結論: ファイルの「中身」に触れる前に、必ず「ファイルそのものの属性と名前」によるゲートキーパー(検問所)を設置しなければならない。
2. 設計思想:フィルタリング・フレームワークの構築
優れた設計とは、「ビジネスロジック」と「ファイル走査ロジック」を分離することである。
「特定の拡張子だけを処理したい」という要求に対し、`If` 文をループの中にベタ書きするのは三流の仕事だ。そうではなく、「対象とするファイルか否かを判定する判定関数」を独立させ、それを走査エンジンに注入する構成を目指すべきである。
堅牢なフィルタリングの3要素
1. 名前パターンによる除外: `~$` や特定の接頭辞を弾く。
2. 拡張子による限定: 必要な拡張子(`.xlsx`, `.csv`等)のみを許可する。
3. 属性による検閲: `Hidden` 属性を持つファイルを無視する。
3. プロダクション・グレードの実装例
以下に、実務でそのまま利用可能な、保守性と堅牢性を極限まで高めたVBScriptのコードを示す。このコードは、単なるループではなく、「判定ロジックの集約」に焦点を当てている。
'==============================================================================
' Project: Robust File Scanner Framework
' Author: Chief Architect
' Description: FSOを用いた、一時ファイルや不要ファイルを排除する堅牢なファイル走査
'==============================================================================
Option Explicit
' --- 設定領域 (Configuration) ---
Const EXCLUDED_PREFIX = "~$" ' Officeの一時ファイル接頭辞
Const TARGET_EXTENSION = ".csv" ' 処理対象の拡張子 (例: .csv)
' --------------------------------
Dim fso, targetFolder, targetPath
Set fso = CreateObject("Scripting.FileSystemObject")
' テスト実行用のパス(環境に合わせて変更すること)
targetPath = "C:\Your\Work\Directory"
If fso.FolderExists(targetPath) Then
Call ExecuteFileProcessing(targetPath)
Else
WScript.Echo "Error: Target folder does not exist."
End If
'------------------------------------------------------------------------------
' メインプロセス:ファイル走査エンジン
'------------------------------------------------------------------------------
Sub ExecuteFileProcessing(strPath)
Dim objFolder, objFile
Set objFolder = fso.GetFolder(strPath)
WScript.Echo "Starting scan in: " & strPath
For Each objFile In objFolder.Files
' 【重要】ここでフィルタリングを行う。
' ビジネスロジック(ファイル処理)に入る前に、ノイズを徹底的に排除する。
If IsValidTargetFile(objFile) Then
' ここに実際の業務処理を記述する
ProcessBusinessLogic objFile
End If
Next
WScript.Echo "Scan completed."
End Sub
'------------------------------------------------------------------------------
' 判定関数:フィルタリング・ゲートキーパー
' ここに「何を処理し、何を無視するか」の全知見を集約する。
'------------------------------------------------------------------------------
Function IsValidTargetFile(objFile)
Dim strName, strExt, lngAttr
IsValidTargetFile = False ' デフォルトは拒否
strName = objFile.Name
strExt = fso.GetExtensionName(strName)
lngAttr = objFile.Attributes
' 1. 接頭辞チェック (Office Temp Files)
' Left関数による高速な接頭辞判定
If Left(strName, Len(EXCLUDED_PREFIX)) = EXCLUDED_PREFIX Then
Exit Function
End If
' 2. 属性チェック (Hidden / System Files)
' Hidden属性(2)やSystem属性(4)が含まれている場合はスキップ
' bitwise AND を用いた判定が最も確実
If (lngAttr And 2) Or (lngAttr And 4) Then
Exit Function
End If
' 3. 拡張子チェック
' 大文字小文字を区別しないよう、小文字に統一して比較
If LCase(strExt) <> LCase(TARGET_EXTENSION) Then
Exit Function
End If
' すべての関門を突破した場合のみ、Trueを返す
IsValidTargetFile = True
End Function
'------------------------------------------------------------------------------
' 業務ロジック:実際の処理
'------------------------------------------------------------------------------
Sub ProcessBusinessLogic(objFile)
On Error Resume Next ' 個別のファイル処理でのエラーが全体を止めないようにする
WScript.Echo "Processing: " & objFile.Name
' 例: ファイルを開いて読み取る処理
' ここでエラーが発生しても、IsValidTargetFileが適切なら発生確率は低い
Dim ts
Set ts = objFile.OpenTextFile(1) ' ForReading
If Err.Number <> 0 Then
WScript.Echo " [ERROR] Could not open file: " & Err.Description
Err.Clear
Else
' --- ここに実際のデータ抽出ロジックを記述 ---
' WScript.Echo " Content: " & ts.ReadLine
ts.Close
End If
On Error GoTo 0 ' エラーハンドリングを元に戻す
End Sub
4. アーキテクトからの警告:データベース連携とログ出力
このスクリプトをさらに大規模なシステム(例えば、スキャン結果をSQL ServerやAccessのDBに書き込む仕組み)に組み込む場合、以下の2点に細心の注意を払え。
① トランザクションの境界線
ファイル走査中にDBへの書き込みを行う場合、「1ファイル=1トランザクション」を原則とせよ。
もし、1000個のファイルを処理するループ全体を一つのトランザクションで囲んでしまうと、999個目がエラーになった瞬間に、それまでの998個の正常な処理までロールバックされる。これは業務自動化において致命的な設計ミスだ。
② ログの「粒度」
エラーが発生した際、単に `WScript.Echo` で表示して終わらせるな。
「どのパスの、どのファイルが、どのような属性(Attributes)を持っていて、どのようなエラーコードで失敗したか」を、タイムスタンプと共にログファイルへ出力する仕組みを持て。デバッグのスピードは、ログの解像度に比例する。
まとめ:プロフェッショナルの作法
VBScriptによる自動化は、一見すると古臭く、単純に見える。しかし、その裏側にある「例外への備え」や「オブジェクトのライフサイクル管理」には、エンジニアリングの真髄が詰まっている。
- ノイズを先に殺せ。
- 判定ロジックを分離せよ。
- 属性(Attributes)を疑え。
これらを遵守するだけで、あなたのスクリプトは「ただ動くコード」から、「信頼されるシステム」へと昇華する。健闘を祈る。
