如何在Windows中压缩30天以上的日志文件?

时间:2018-12-12 10:23:09

标签: windows powershell scripting

我在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 *文件。 期望我可以通过在上述命令中添加管道符号来使用单个命令。

2 个答案:

答案 0 :(得分:3)

如果您具有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"
}

答案 1 :(得分:0)

如果您使用的是Powershell的较旧版本,则可以使用ZipFileExtensions的CreateEntryFromFile方法,但是,如果要使健壮的脚本在无人值守的情况下运行,则有很多注意事项。

在测试为此目的开发的script的几个月中,我遇到了一些使这个小问题变得更加复杂的问题:

  1. 是否将锁定任何文件?如果是这样,CreateEntryFromFile可能会失败。
  2. 您知道Zip存档中可以有相同文件的多个副本吗?提取它们比较困难,因为您不能将它们放在同一文件夹中。我的脚本检查文件路径和已存档文件的时间戳(由于Zip格式丢失日期精度,+ /-2秒),以确定它是否已被存档,并且不创建重复副本。
  3. 是否在具有夏令时的时区中创建文件?压缩格式不会保留该属性,并且在未压缩时可能会丢失或增加一个小时。
  4. 如果原始文档已成功存档,您要删除吗?
  5. 如果由于文件锁定/丢失或路径太长而导致失败,该过程应该继续吗?
  6. 任何错误都会使您留下无法使用的zip文件吗?您需要Dispose()存档才能完成。
  7. 您要保留多少个档案?我希望每个月运行一次,将新条目添加到现有的zip中。
  8. 是否要保留相对路径?这样做将部分消除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).TrimStart(".\")
        $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)}