嗨,Scripting Guy!

Hey,Scripting Guy!

歡迎使用全新的 TechNet 專欄,Microsoft Scripting Guys 會在此為您解答有關系統管理指令碼的常見問題。您有關於系統管理指令碼方面的問題嗎?請將電子郵件傳送到 scripter@microsoft.com。我們無法保證能夠逐一回答每個問題,不過我們會盡力而為。

今天的問題:如何依據檔案的建立日期重新命名檔案?


如何依據檔案的建立日期重新命名檔案?

嗨,Scripting Guy!我有個資料夾,裡面有許多記錄檔。我想要依據建立檔案的日期重新命名每個檔案;例如,假設檔案是在 2004 年 12 月 1 日建立的,我想將它重新命名為 20041201.log。這樣可能嗎?

-- CK

CK,您好。談到指令碼,我們可以說它幾乎是無所不能 (當然,不可能做到的就是例外囉!)。雖然在許多情況下日期在 WMI 指令碼中並不太好處理,不過這一次 WMI 的日期卻能讓這件工作「更容易」執行。

為了方便說明,讓我們將這件工作分成兩個部份。首先,要如何列出資料夾中的所有檔案,以及它們的建立日期?方法很多,譬如使用類似下面的指令碼:

strComputer = "."
Set objWMIService = GetObject("winmgmts\\" & strComputer & "\root\cimv2")
Set FileList = objWMIService.ExecQuery _
    ("ASSOCIATORS OF {Win32_Directory.Name='C:\Logs'} Where " _
        & "ResultClass = CIM_DataFile")
For Each objFile In FileList
    Wscript.Echo objFile.CreationDate
Next

在這段指令碼中,我們繫結到 WMI 服務,然後利用關聯查詢傳回資料夾 C:\Logs 中所有檔案的集合。如果您不太熟悉關聯查詢,其實它們就如同這個名稱所代表的意思一樣:它們可以讓您將兩個個別的 WMI 類別產生關聯,在這個例子裡,就是 Win32_Directory 和 CIM_DataFile。兩者產生關聯之後的效果,就可以讓我們取得關聯資料夾中所有檔案的集合。

取得所有檔案之後,我們只需要建立一個 For-Each 迴圈,對每一個檔案呼叫 CreationDate 屬性的值。由於 WMI 是使用 UTC (國際標準時間) 格式,所以我們會取得類似下面這樣的值:

20041201202723.695209-480

沒錯,看起來怪怪的,不過卻剛好符合我們的需要:前面八個字元對應到年、月和日,剛好就是我們要用作檔名的值。您不是想要像 20041201 這樣的檔名嗎?仔細看看 UTC 日期中的前面八個字元:20041201202723.695209-480。正如所需,一字不差。

事實上,要取得檔名,只要用 VBScript 的 Left 函式擷取 UTC 日期中的前面八個字元就可以了。下面這段修改後的指令碼會擷取前面八個字元、將它們儲存在名叫 strDate 的變數中,然後呼叫「這個」值:

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set FileList = objWMIService.ExecQuery _
    ("ASSOCIATORS OF {Win32_Directory.Name='c:\Logs'} Where " _
        & "ResultClass = CIM_DataFile")
For Each objFile In FileList
    strDate = Left(objFile.CreationDate, 8)
    Wscript.Echo strDate
Next

步驟一談了半天,現在該談談第二 (也是最後) 步驟了:重新命名每一個檔案。讓我先示範這段指令碼,然後再解釋它的運作方式:

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set FileList = objWMIService.ExecQuery _
    ("ASSOCIATORS OF {Win32_Directory.Name='C:\Logs'} Where " _
        & "ResultClass = CIM_DataFile")
For Each objFile In FileList
    strDate = Left(objFile.CreationDate, 8)
    strNewName = objFile.Drive & objFile.Path & strDate & "." & "log"
    errResult = objFile.Rename(strNewName)
Next

這裡比較要注意的地方是建構新檔名。使用 WMI 重新命名檔案的時候,不能只是指定新檔名,我們不能只是將檔案 Wednesday.log 重新命名為 20041201.log。我們必須指定整個路徑,並且將檔案重新命名為 C:\Logs\20041201.log。下面這行程式碼做的就是這些動作:

strNewName = objFile.Drive & objFile.Path & strDate & ".log"

我們在這裡所要做的,是將檔案所在的磁碟機 (C:)、該磁碟機上的資料夾路徑 (\Logs\)、檔案的新檔名 (20041201,也就是變數 strDate 的值),以及新的副檔名 (.log) 結合起來。將這些放在一起之後,就可得到 C:\Logs\20041201.log,也就是我們所要指定給檔案的完整路徑。接下來,我們必須呼叫 Rename 方法,就可以變更檔名了。

順便一提,只是為了重新命名檔案而必須提供整個路徑,感覺好像有點怪怪的。不過這樣做也有它的好處,因為您可以用相同的方式將檔案移到其他資料夾。例如,假設您不只要重新命名這些檔案,還要將它們移到名叫 C:\Backups 的資料夾裡。在這種情況下,您可以用類似上面的指令碼,但是把目前的檔案路徑 (\Logs\) 代換成新的路徑 (\Backups\)。執行這個指令碼的時候,這些檔案不僅會重新命名,而且會移到 C:\Backups:

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set FileList = objWMIService.ExecQuery _
    ("ASSOCIATORS OF {Win32_Directory.Name='C:\Logs'} Where " _
        & "ResultClass = CIM_DataFile")
For Each objFile In FileList
    strDate = Left(objFile.CreationDate, 8)
    strNewName = objFile.Drive & "\Backups\" &  strDate & ".log"
    errResult = objFile.Rename(strNewName)
Next

我們所示範的指令碼唯一的問題在於,假如有些檔案具有相同的建立日期,這個指令碼就不靈了;到最後,這個指令碼會嘗試建立兩個相同名稱 (譬如 20041201.log) 的檔案,但是檔案系統卻不允許在同一個資料夾裡有相同名稱的檔案。如果發生這種情況,下面的範例指令碼可以在實際重新命名檔案之前,先檢查檔名是不是唯一的。例如,假設資料夾裡已經有一個名叫 20041201.log 的檔案。如果發生這種情況,這個指令碼 (今天沒有時間詳細說明) 將會建立一個新的檔名:20041201_2.log,並且檢查「這個」檔案是否存在。如果這個檔案也存在,它會再嘗試 20041201_3.vbs,就這樣一直試下去,直到能建立唯一的名稱為止。到這時,它才會實際重新命名這個檔案:

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set FileList = objWMIService.ExecQuery _
    ("ASSOCIATORS OF {Win32_Directory.Name='c:\Logs'} Where " _
        & "ResultClass = CIM_DataFile")
For Each objFile In FileList
    strDate = Left(objFile.CreationDate, 8)
    strNewName = objFile.Drive & objFile.Path & _
       strDate & "." & "log"
    strNameCheck = Replace(strNewName, "\", "\\")
    i = 1
    Do While True
        Set colFiles = objWMIService.ExecQuery _
            ("Select * from Cim_Datafile Where Name = '" & strNameCheck & "'")
        If colFiles.Count = 0 Then
            errResult = objFile.Rename(strNewName)
            Exit Do
        Else
            i = i + 1
            strNewName = objFile.Drive & objFile.Path & _
                strDate & "_" & i & "." & "log"
            strNameCheck = Replace(strNewName, "\", "\\")
        End If
    Loop
Next

沒錯,這是有點複雜,這也是我們要大家「且看下回分解」的原因。怎麼...您不會真的認為我們會為了這個專欄發加班費吧!


如需詳細資訊

查看嗨,Scripting Guy!- 過往文件

 

回到頁首 回到頁首