Если у вас более старая версия Powershell, вы можете использовать метод CreateEntryFromFile в ZipFileExtensions, но есть множество соображений, если вам нужен надежный скрипт, который запускается без присмотра.
За месяцы тестирования сценария , разработанного для этой цели, я столкнулся с некоторыми проблемами, которые усложнили эту небольшую проблему:
- Будет ли любой из файловбыть заблокирован?CreateEntryFromFile может завершиться ошибкой, если это так.
- Знаете ли вы, что у вас может быть несколько копий одного и того же файла в Zip-архиве?Их сложнее извлечь, потому что вы не можете поместить их в одну папку.Мой сценарий проверяет путь к файлу и отметку времени архивного файла (+/- 2 секунды из-за потери точности даты в формате Zip), чтобы определить, был ли он уже заархивирован, и не создает дубликат.
- Созданы ли файлы в часовом поясе с переходом на летнее время?Zip-формат не сохраняет этот атрибут и может потерять или получить час без сжатия.
- Хотите удалить оригинал, если он был успешно заархивирован?
- Если процесс не удастся из-за заблокированного / отсутствующего файла или очень длинного пути, должен ли процесс продолжаться?
- Любая ошибка оставит вас с непригодным для использования zip-файлом?Вам необходимо удалить () архив, чтобы завершить его.
- Сколько архивов вы хотите сохранить?Я предпочитаю один в месяц выполнения, добавляя новые записи в существующий почтовый индекс.
- Хотите сохранить относительный путь?Это частично устранит проблему дубликатов внутри zip-файла.
Сценарий Марка Врэгга должен работать, если вас не волнуют эти проблемы, и у вас есть Powershell 5, но он создает почтовый индекс для каждого журнала, что может не соответствовать вашим ожиданиям.
Вот текущая версия скрипта - на случай, если 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
Вот командная строка, которая сжимает все файлы server.log * старше 30 дней под текущейпапка:
.\ArchiveOldLogs.ps1 -FileSpecs @("server.log*") -Filter { $_.LastWriteTime -lt (Get-Date).AddDays(-30)}