PowerShell 小程式 - 壓縮並刪除一個月前的 Log 檔
4 | 4,746 |
Web 主機上 Log 成長頗快,我想寫個小程序將時間較久的 Log 以月為單位壓縮儲存節省空間。 實測壓縮後體積只剩 3%,足足減少 30 倍有餘,估計可省下大量空間。
類似的工具之前寫過 C# 版,這回試試用 PowerShell 解決。
Log 檔一天一個,壓縮排程設在每月第一天。執行時前一個月的 Log 檔不動,再前一個月起的 Log 檔,以月為單位壓成一個檔案。
我想到的演算邏輯為:先用 DateTime.Today 取當月第一天(這樣子就算不是月初跑也沒關係)後 AddMonths(-1) 算出前一個月的第一天作為 保留起始日。DIR *.log 篩選保留起始日之前的所有 Log 檔名,Group By yyyy-MM 再 Distinct 取得待處理月份字串清單。 (第一次執行會將一個月以上的 Log 全部做完) 接著跑迴圈將 yyyy-MM*.log 檔案用 7z.exe 最高壓縮比(-mx9)壓成 yyyy-MM.7z,壓縮完成後刪除 yyyy-MM*.log。
執行結果會像這樣:
前面說的邏輯要我用 C# 寫跟喝水一樣簡單,轉成不熟悉的 PowerShell 得花點腦筋,但只是要爬點文,並不難搞定:
- 保留起始日直接用 .NET DateTime 物件計算,在 PS 可寫 [DateTime]::Today.AddDays(...).AddMonths()。
- Get-ChildItem *.log 相當於 DIR *.log
- 用 Pipeline 串接 Where,用 -lt 比對檔名 ($_.Name) 過濾檔名小於保留起始日的檔案
- foreach { $_.Name.Substring(0, 7) } 將檔案物件轉成 yyyy-MM 字串陣列
- 對 yyyy-MM 字串陣列執行 Sort-Object -Unique,可得到相當於 LINQ Distinct() 的效果
- 最後跑 foreach 迴圈,透過 & "7z.exe" 呼叫外部 EXE 程式,打包參數需用 Split(' ') 拆成字串陣列傳入
- Remove-Item 將 yyyy-MM*.log 刪除
完整程式範例如下:
$reserveDate = [DateTime]::Today.AddDays(-[DateTime]::Today.Day+1).AddMonths(-1).ToString("yyyy-MM-dd");
$cmd = "D:\Tools\7z.exe";
Get-ChildItem *.log | Where { $_.Name -lt $reserveDate } |
foreach {$_.Name.Substring(0, 7)} | Sort-Object -Unique |
foreach {
$params = "a -mx9 $_.7z $_*.log".Split(" ");
& $cmd $params;
Remove-Item -Path "$_*.log"
}
如果是 PowerShell 5.0+,其內建 Compress-Archive 可產生 ZIP 檔不需仰賴外部程式,但我擔心部署主機 PowerShell 版本不夠新,還是決定用 7z.exe 減少部署需求。
另外,以上程式可存成 ArchiveLogsMonthAgo.ps1 檔,在最前方加入一行接收參數切換到 Log 所在路徑執行壓縮,便可用於多個 Log 資料夾。
Set-Location -Path $args[0];
PowerShell example to archive log files earlier than one month.
Comments
# by Pika
如果不考慮練習 PowerShell 為目的的話, 這個 case 我會考慮用 PowerShell 產生時間戳記, 比如 yyyy-MM, 然後呼叫 7z.exe 壓縮 yyyy-MM-*.log 再加上刪除原檔的 switch 參數, 比較簡潔一點的感覺, 供參考. https://sevenzip.osdn.jp/chm/cmdline/switches/sdel.htm
# by Jeffrey
to Pika, 這招好! 學習了。感謝分享。
# by IT DOG
用 gz 會方便 過7z
# by ALIEN.SUN
謝謝您分享,這功能用forfiles+7Z的CMD寫過,您的更是方便啊!!