2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【VBS】指定したフォルダの最新ファイルを取得する

Posted at

はじめに

監視ソフトのスクリプト(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年分程度残して削除する運用にすればいいでしょう。そうすれば高速版にしなくても速度的には問題にはならないです。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?