【自動削除】DateLastModifiedプロパティを用いた「30日以上前の旧ログファイル」定期クリーンアップ処理
レガシーシステムの底流を支えるインフラストラクチャにおいて、最も厄介で、かつ見落とされがちなのが「ログファイルの肥大化」である。
ディスク容量の枯渇は、OSのスワップ領域の圧迫、DBのトランザクションログの書き込み失敗、ひいてはシステム全体のクラッシュを引き起こす。
我々は日々、数千・数万のファイルが生成される環境の保守に直面している。
ここで、外部のサードパーティ製ツールや大げさな常駐サービスを導入する必要はない。Windows環境であれば、VBScript(Visual Basic Scripting Edition)とFileSystemObject(FSO)を組み合わせることで、極めて軽量かつ堅牢な自動クリーンアップ機構をOSのネイティブ機能だけで構築できる。
今回は、`DateLastModified` プロパティと `DateDiff` 関数を駆使し、30日以上経過した不要なログファイルを確実に刈り取る、実運用に耐えうるスクリプトの全貌を解説する。
—
1. アーキテクチャの要件と技術的アプローチ
単に「古いファイルを消す」だけのスクリプトであれば、数行で書くことができる。しかし、シニアエンジニアが構築するべきは、以下の要件を満たす「止まらないスクリプト」である。
- メモリリークの完全排除: COMオブジェクトの適切な解放(`Set obj = Nothing`)の徹底。
- 例外処理(Errオブジェクト)の網羅: 削除対象ファイルが別プロセス(ログ出力中など)によってロックされている場合のフリーズ回避。
- タイムゾーンと日付計算の厳密性: `Date` 型の減算における時分秒の考慮。
- 可観測性(ログ出力): 何を削除し、何に失敗したかをトレースできる仕組み。
DateLastModifiedの罠と仕様
`File` オブジェクトの `DateLastModified` は、ファイルシステムが保持する最終更新タイムスタンプを返す。ここで注意すべきは、これがVariant型(内部表現:VT_DATE)であるという点だ。
VBScriptの動的型付けに甘えたコードを書くと、暗黙の型変換エラーや予期せぬ日付のズレを引き起こす。厳密な型比較と、`DateDiff` による日数計算が不可欠となる。
—
2. 実装コード:堅牢なるクリーンアップ・エンジン
以下のコードは、実運用環境(タスクスケジューラでのバッチ実行)を想定してチューニングされたプロダクションコードである。そのまま `.vbs` ファイルとして保存し、利用可能である。
Option Explicit
‘ ==============================================================================
‘ スクリプト名: LogCleaner.vbs
‘ 概要 : 指定フォルダ内の30日以上前に更新されたログファイルを再帰的に削除する
‘ 依存関係 : Windows Script Host (WSH), FileSystemObject (FSO)
‘ ==============================================================================
Call Main()
Sub Main()
‘ — 設定エリア —
Dim targetFolder
targetFolder = “C:\Application\Logs” ‘ 対象ログフォルダのパス
Dim retentionDays
retentionDays = 30 ‘ 保持期間(日)
Dim fso, targetDir
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ フォルダの存在確認
If Not fso.FolderExists(targetFolder) Then
WScript.Echo “[ERROR] 対象フォルダが存在しません: ” & targetFolder
Set fso = Nothing
WScript.Quit 1
End If
Set targetDir = fso.GetFolder(targetFolder)
WScript.Echo “[INFO] クリーンアップ処理を開始します: ” & targetFolder
‘ 再帰的な削除処理の実行
Dim deletedCount, errorCount
deletedCount = 0
errorCount = 0
Call CleanFiles(targetDir, retentionDays, fso, deletedCount, errorCount)
‘ — クリーンアップの完了報告 —
WScript.Echo “[INFO] 処理完了. 削除ファイル数: ” & deletedCount & “, エラー数: ” & errorCount
‘ オブジェクトの明示的解放(メモリ最適化)
Set targetDir = Nothing
Set fso = Nothing
End Sub
‘ ——————————————————————————
‘ 再帰的ファイル走査および削除サブルーチン
‘ ——————————————————————————
Sub CleanFiles(folder, thresholdDays, fso, ByRef delCount, ByRef errCount)
On Error Resume Next ‘ ファイルロック等による異常終了を防ぐため局所的に有効化
Dim subFolder, file
Dim currentDate, fileDate, diffDays
currentDate = Now()
‘ 1. 現在のフォルダ内のファイルを走査・判定
For Each file in folder.Files
Err.Clear
fileDate = file.DateLastModified
If Err.Number <> 0 Then
WScript.Echo “[WARN] 最終更新日の取得に失敗しました: ” & file.Path & ” (Error: ” & Err.Description & “)”
errCount = errCount + 1
Else
‘ 現在時刻との日数の差分を計算 (“d” は日単位)
diffDays = DateDiff(“d”, fileDate, currentDate)
If diffDays >= thresholdDays Then
‘ 削除実行
file.Delete True ‘ True = 読み取り専用属性も強制削除
If Err.Number = 0 Then
WScript.Echo “[DELETED] (” & diffDays & “日前) ” & file.Path
delCount = delCount + 1
Else
WScript.Echo “[ERROR] 削除失敗(ロック中の可能性): ” & file.Path & ” (Error: ” & Err.Description & “)”
errCount = errCount + 1
End If
End If
End If
Next
‘ 2. サブフォルダを再帰的に巡回
For Each subFolder in folder.SubFolders
Call CleanFiles(subFolder, thresholdDays, fso, delCount, errCount)
Next
On Error GoTo 0 ‘ エラー制御をデフォルトに戻す
End Sub
—
3. チーフアーキテクトが解説するコードの急所
① `On Error Resume Next` の局所的制御と `Err.Clear`
バッチ処理において「1つのファイルが削除できない(例: ログ出力中のプロセスが掴んでいる)という理由で、スクリプト全体が異常終了する」ことは絶対に許されない。
そのため、ファイル削除のループ内でのみエラーをトラップし、カウンタ(`errCount`)に集計した上で次のファイルの処理へ継続(Resume)する設計にしている。
さらに、ループの各イテレーションの冒頭で `Err.Clear` を必ず呼ぶこと。VBScriptのErrオブジェクトは明示的にクリアしない限り、前のエラー情報を保持し続けるという悪名高い仕様があるためだ。
② 強制削除フラグ (`file.Delete True`)
ログファイルには誤って「読み取り専用(Read-Only)」属性が付与されるケースが散見される。`file.Delete` の引数に `True`(Forceパラメータ)を渡すことで、属性起因の削除失敗を水際で防ぐ。
③ COMオブジェクトのライフサイクルとメモリマネジメント
VBScriptのガベージコレクションは参照カウント方式(Reference Counting)に依存している。特に長時間のループや再帰処理において、オブジェクト変数の解放漏れは メモリリーク(Memory Leak) を引き起こし、WSCRIPT.EXEのプロセス肥大化を招く。
スクリプトの末尾、あるいはスコープの抜け際において `Set fso = Nothing` を明示的に実行し、COMコンポーネントの解放を確実に行うことがプロフェッショナルの作法である。
—
4. 運用・保守におけるベストプラクティス
1. タスクスケジューラでの実行権限:
ファイル削除権限(NTFSアクセス権)を持つ専用のサービスアカウント、あるいは適切な権限を持つローカルシステムアカウントで実行すること。
2. 実行結果のロディグ(リダイレクト):
コンソール出力を伴うため、タスクスケジューラから実行する際は標準出力をファイルにリダイレクトさせること。
> `cscript.exe //Nologo C:\Scripts\LogCleaner.vbs >> C:\Scripts\LogCleaner.log 2>&1`
3. 空フォルダの残留問題:
上記のコードはファイル単位での削除を行うため、ログが全て消えたサブフォルダが空のまま残る場合がある。もしフォルダ構造自体のクリーンアップが必要な場合は、ファイル削除後にフォルダ内のファイル数をカウントし、`0` であれば `folder.Delete` を実行するロジックを拡張すると完璧である。
レガシーな技術と侮るなかれ。VBScriptはOSの深部にダイレクトにアプローチできる極めて軽量なランタイムである。適切な例外処理と設計思想を纏わせることで、モダンな言語で書かれた重厚なデーモンにも匹敵する堅牢性を発揮する。現場のインフラ安定化に、ぜひこの知見を役立ててほしい。
