【実務・中級編】【実務中級者向け】FSOのファイル走査時に一時ファイル(.tmp / ~$)や不要ファイルをスキップするフィルタリング – VBScript (Visual Basic Scripting Edition)解析バイブル

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

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)を疑え。

これらを遵守するだけで、あなたのスクリプトは「ただ動くコード」から、「信頼されるシステム」へと昇華する。健闘を祈る。

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