如何在Windows中压缩超过30天的日志文件?

h22fl7wq  于 2023-02-20  发布在  Windows
关注(0)|答案(2)|浏览(270)

我编写了下面的PowerShell脚本来压缩超过30天的日志:

$LastWrite=(get-date).AddDays(-30).ToString("MM/dd/yyyy")

Get-ChildItem -Filter "server.log*" -Recurse -File | Where-Object 
{$_.LastWriteTime -le $LastWrite}

现在,我无法在PowerShell中获得压缩命令,通过该命令我可以压缩(zip/tar)超过30天的server.log * 文件。希望通过在上面的命令中添加管道符号来使用单个命令。

mf98qq94

mf98qq941#

如果您有PowerShell版本5或更高版本,则可以使用Compress-Archive cmdlet压缩文件:

$LastWrite = (get-date).AddDays(-30)

$Files = Get-ChildItem -Filter "server.log*" -Recurse -File | Where-Object {$_.LastWriteTime -le $LastWrite}

ForEach ($File in $Files) {
    $File | Compress-Archive -DestinationPath "$($File.fullname).zip"
}
zpqajqem

zpqajqem2#

如果您使用的是旧版本的Powershell,则可以使用ZipFileExtensions的CreateEntryFromFile方法,但是如果您想要一个健壮的脚本,并且无人值守,则需要考虑很多事项。
在测试为此目的开发的script的几个月中,我遇到了一些问题,这些问题使这个小问题变得更加复杂:
1.是否会锁定任何文件?如果锁定,CreateEntryFromFile可能会失败。
1.你知道你可以在一个Zip压缩文件中拥有同一个文件的多个副本吗?因为你不能把它们放在同一个文件夹中,所以很难提取它们。我的脚本会检查文件路径和存档文件的时间戳(+/- 2秒,因为Zip格式中丢失了日期精度),以确定它是否已经被存档,并且不会创建副本。
1.文件是在夏令时时区创建的吗?Zip格式不保留该属性,在解压缩时可能会损失或增加一个小时。
1.如果原始文件已成功存档,是否要将其删除?
1.如果由于文件锁定/丢失或路径过长而导致失败,是否应继续该过程?
1.任何错误都会给你留下一个不可用的zip文件吗?你需要Dispose()来完成它。
1.你想保留多少个压缩包?我喜欢每个运行月一个,在现有压缩包中添加新条目。
1.是否要保留相对路径?这样做将部分消除zip文件内的重复问题。
如果您不关心这些问题,并且您有Powershell 5,Mark Wragg的脚本应该可以工作,但是它为每个日志创建一个zip,这可能不是您想要的。
以下是脚本的当前版本-以防GitHub不可用:

#Sends $FileSpecs files to a zip archive if they match $Filter - deleting the original if $DeleteAfterArchiving is true. 
#Files that have already been archived will be ignored. 
param (
   [string] $ParentFolder = "$PSScriptRoot", #Files will be stored in the zip with path relative to this folder
   [string[]] $FileSpecs = @("*.log","*.txt","*.svclog","*.log.*"), 
   $Filter = { $_.LastWriteTime -lt (Get-Date).AddDays(-7)}, #a Where-Object function - default = older than 7 days
   [string] $ZipPath = "$PSScriptRoot\archive-$(get-date -f yyyy-MM).zip", #create one archive per run-month - it may contain older files 
   [System.IO.Compression.CompressionLevel]$CompressionLevel = [System.IO.Compression.CompressionLevel]::Optimal, 
   [switch] $DeleteAfterArchiving = $true,
   [switch] $Verbose = $true,
   [switch] $Recurse = $true
)
@( 'System.IO.Compression','System.IO.Compression.FileSystem') | % { [void][System.Reflection.Assembly]::LoadWithPartialName($_) }
Push-Location $ParentFolder #change to the folder so we can get relative path
$FileList = (Get-ChildItem $FileSpecs -File -Recurse:$Recurse  | Where-Object $Filter) #CreateEntryFromFile raises UnauthorizedAccessException if item is a directory
$totalcount = $FileList.Count
$countdown = $totalcount
$skipped = @()
Try{
    $WriteArchive = [IO.Compression.ZipFile]::Open( $ZipPath, [System.IO.Compression.ZipArchiveMode]::Update)
    ForEach ($File in $FileList){
        Write-Progress -Activity "Archiving files" -Status  "Archiving file $($totalcount - $countdown) of $totalcount : $($File.Name)"  -PercentComplete (($totalcount - $countdown)/$totalcount * 100)
        $ArchivedFile = $null
        $RelativePath = (Resolve-Path -LiteralPath "$($File.FullName)" -Relative) -replace '^.\\'
        $AlreadyArchivedFile = ($WriteArchive.Entries | Where-Object {#zip will store multiple copies of the exact same file - prevent this by checking if already archived. 
                (($_.FullName -eq $RelativePath) -and ($_.Length -eq $File.Length) )  -and 
                ([math]::Abs(($_.LastWriteTime.UtcDateTime - $File.LastWriteTimeUtc).Seconds) -le 2) #ZipFileExtensions timestamps are only precise within 2 seconds. 
            })     
        If($AlreadyArchivedFile -eq $null){
            If($Verbose){Write-Host "Archiving $RelativePath $($File.LastWriteTimeUtc -f "yyyyMMdd-HHmmss") $($File.Length)" }
            Try{
                $ArchivedFile = [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($WriteArchive, $File.FullName, $RelativePath, $CompressionLevel)
            }Catch{
                Write-Warning  "$($File.FullName) could not be archived. `n $($_.Exception.Message)"  
                $skipped += [psobject]@{Path=$file.FullName; Reason=$_.Exception.Message}
            }
            If($File.LastWriteTime.IsDaylightSavingTime() -and $ArchivedFile){#HACK: fix for buggy date - adds an hour inside archive when the zipped file was created during PDT (files created during PST are not affected).  Not sure how to introduce DST attribute to file date in the archive. 
                $entry = $WriteArchive.GetEntry($RelativePath)    
                $entry.LastWriteTime = ($File.LastWriteTime.ToLocalTime() - (New-TimeSpan -Hours 1)) #TODO: This is better, but maybe not fully correct. Does it work in all time zones?
            }
        }Else{#Write-Warning "$($File.FullName) is already archived$(If($DeleteAfterArchiving){' and will be deleted.'}Else{'. No action taken.'})" 
            Write-Warning "$($File.FullName) is already archived - No action taken." 
            $skipped += [psobject]@{Path=$file.FullName; Reason="Already archived"}
        }
        If((($ArchivedFile -ne $null) -and ($ArchivedFile.FullName -eq $RelativePath)) -and $DeleteAfterArchiving) { #delete original if it's been successfully archived. 
            Try {
                Remove-Item $File.FullName -Verbose:$Verbose
            }Catch{
                Write-Warning "$($File.FullName) could not be deleted. `n $($_.Exception.Message)"
            }
        } 
        $countdown = $countdown -1
    }
}Catch [Exception]{
    Write-Error $_.Exception
}Finally{
    $WriteArchive.Dispose() #close the zip file so it can be read later 
    Write-Host "Sent $($totalcount - $countdown - $($skipped.Count)) of $totalcount files to archive: $ZipPath"
    $skipped | Format-Table -Autosize -Wrap
}
Pop-Location

下面的命令行将压缩当前文件夹下超过30天的所有server.log* 文件:

.\ArchiveOldLogs.ps1 -FileSpecs @("server.log*") -Filter { $_.LastWriteTime -lt (Get-Date).AddDays(-30)}

相关问题