はじめに
監視ソフトのスクリプト(VBScript)で、バックアップソフトのログを監視して正常か異常かを判定する処理をしています。
最近になって、バックアップは正常に行われているのに異常の通知がされることが数回ありました。
原因を調べてみると、監視ソフト側でスクリプトの処理が60秒以上となりタイムアウトエラーになっていました。
ログフォルダを見てみると、2015年から1日1回出力されており単純計算でも365日x5=1825件以上はあるわけです。でも、正常か異常かを判断するのは最新のログファイル1つだけです。この最新のログファイルを見つける処理に時間がかかってしまっていたわけです。
現行版
ログフォルダの最新ファイルを取得するのに、WMIのCIM_DataFileクラスを使用してました。
しかし、WQL(WMI Query Language)は、ソートサポートしてないのでORDER BY
句は使えません。
最新ファイルを取得するには、ファイル数分の更新日時を比較して最新ファイルを求めることになります。
AIPLOG_PATH = "C:\Program Files (x86)\NetJapan\ActiveImage Protector\logs"
MsgBox GetLatestFileName(AIPLOG_PATH, "log")
' Logフォルダの最終更新日時が最新のログファイル名を返す
Function GetLatestFileName(path, ext)
Dim fso, locator, service, files, drive, latest, latestFile
Set fso = CreateObject("Scripting.FileSystemObject")
Set locator = CreateObject("WbemScripting.SWbemLocator")
Set service = locator.ConnectServer
drive = fso.GetDriveName(path)
path = Replace(path, drive, "") & "\"
Set files = Service.ExecQuery("SELECT * FROM CIM_DataFile " _
& "WHERE Drive = '" & drive & "'" _
& " AND Path = '" & Replace(path, "\", "\\") & "'" _
& " AND Extension = '" & ext & "'")
latest = ""
latestFile = ""
For Each f In files
If Latest < f.LastModified Then
latest = f.LastModified
latestFile = f.Filename & "." & f.Extension
End If
Next
GetLatestFileName = latestFile
End Function
この方法は、ファイル数が多くなってくれば時間がかかってしまいます。1800件くらい大した数じゃないと思っていたのですが、予想に反して異様に遅いですね。ただ初回が遅くて2回目はキャッシュが効くのか数秒で結果が返ってきます。
結局、1年分のログファイルだけにすることでタイムアウトエラーにはならなくなりました。
高速版
大量ファイルがあっても高速に最新ファイルを取得する方法があります。それは、DOSのDirコマンドを使用することです。
Dirコマンドには、並び替えの方法を指定する「/o」オプションがあります。
DIR [パス] /o-d
とすれば日時の降順になります。
その他に「/b」オプションでファイル名のみ、「/a:-d」オプションでディレクトリ以外の一覧にしています。
AIPLOG_PATH = "C:\Program Files (x86)\NetJapan\ActiveImage Protector\logs"
MsgBox GetLatestFileName(AIPLOG_PATH, "log")
' Logフォルダの最終更新日時が最新のログファイル名を返す
Function GetLatestFileName(path, ext)
Dim wshShell, exec, line
Set wshShell = CreateObject("WScript.Shell")
Set exec = WshShell.Exec("cmd /c dir /a-d /b /o-d """ & path & "\*." & ext & """")
exec.StdIn.Close()
Do While Not exec.Stdout.atEndOfStream
line = Trim(exec.StdOut.ReadLine())
Exit Do
Loop
Set exec = Nothing
Set wshShell = Nothing
GetLatestFileName = line
End Function
最後に
ファイル一覧を取得するならDirコマンドが高速でおすすめです。
以前、大量ファイルがあるフォルダのファイル一覧をFileSystemObjectを再帰処理で使用して取得したら全然遅くて、Dirコマンドにしたら数秒で結果が返ってきて驚いたことがあります。
今回に関してはログファイルはずっと保持しておく必要もないので、メンテナンスのタイミングで1年分程度残して削除する運用にすればいいでしょう。そうすれば高速版にしなくても速度的には問題にはならないです。