Powershell — последовательное архивирование файлов цикла foreach

У меня есть несколько серверов IIS, на каждом из которых есть несколько сайтов, и я хочу регулярно архивировать все журналы IIS.

С помощью этого сайта и Google я собрал следующий скрипт powershell:

$files = Get-ChildItem  "D:\logfiles\IIS-Logs\*.log" -Recurse | Where-Object {($_.LastWriteTime -lt (Get-Date).AddDays(-7))} 
foreach ($file in $files) {& 'C:\Program Files\WinRAR\winrar.exe' a -tl -df -m5 "$file.rar" $File}

Проблема с этим сценарием заключается в том, что если есть... скажем... 2000 лог-файлов, он попытается одновременно запустить 2000 копий Winrar, и сервер рухнет. Это было неожиданно. Я ожидал, что он заархивирует файлы по одному. Последовательно.

У кого-нибудь есть идеи, как заставить это работать так, как я хочу?

Я бы очень хотел использовать Winrar вместо встроенной опции Compress-Archive, потому что:

  • Я хочу, чтобы даты файла отражали заархивированный файл, а не дату, когда он был заархивирован.
  • Я хочу, чтобы утилита удаляла файлы после архивирования, потому что утилита не удалит файл, если архивация не удалась.

Я не женат на Winrar, если я могу добиться этого другим способом.


person ecksfiftyone    schedule 10.02.2020    source источник


Ответы (2)


Вы, вероятно, захотите использовать «Start-Process-Wait» вместо использования &. Использование флага -Wait в Start-Process заставляет его ждать завершения, после чего ваша программа будет выполняться последовательно. Прочтите эту статью о том, как использовать Start-Process: лучший процесс запуска PowerShell. Вы также можете использовать командлет Compress-Archive вместо программы командной строки Winrar, которая может быть лучше интегрирована и даст вам лучшую обратную связь в ваших сценариях.

Что-то подобное с WinRAR?

$files = Get-ChildItem  "D:\logfiles\IIS-Logs\*.log" -Recurse | Where-Object {($_.LastWriteTime -lt (Get-Date).AddDays(-7))}
foreach ($file in $files) {
    $args = 'a -tl -df -m5 "' + $file.rar + '" ' + $File
    Start-Process -Wait -filepath 'C:\Program Files\WinRAR\winrar.exe' -ArgumentList $args
}

Или это с помощью Compress-Archive

$files = Get-ChildItem  "D:\logfiles\IIS-Logs\*.log" -Recurse | Where-Object {($_.LastWriteTime -lt (Get-Date).AddDays(-7))}
foreach ($file in $files) {
    Compress-Archive -Path $file.FullName -DestinationPath "$($File.FullName).rar"
{

Вышеупомянутое не проверено, но должно работать, если я не сделал опечатку.

person Matthew    schedule 10.02.2020
comment
$args были неправильными, делая имя rar пустым... но с несколькими настройками это блестяще работает с winrar! Спасибо ($args = 'a -tl -df -m5' + $file +'.rar ' + $File) - person ecksfiftyone; 13.02.2020
comment
Да, синтаксис передачи аргументов программам/процессам, не поддерживающим powershell, немного шаткий. Рад, что вы смогли заставить его работать, хотя! - person Matthew; 14.02.2020

В целях тестирования я создал LogFolder с 3 отдельными папками журналов: Log1, Log2 и Log3. Каждая из этих папок журналов содержит 2000 файлов по 2 МБ каждый. Это команда, которую я запускал для сжатия каждой папки «отдельно».

Вы также можете запускать их последовательно, если производительность слишком низкая (при чтении с одного и того же диска и при записи на один и тот же).


$ElementsInLog = (Get-ChildItem C:\temp\LogFolder\*.txt -Recurse).Length
$ElementsInLog1 = (Get-ChildItem C:\temp\LogFolder\Log1\*.txt -Recurse).Length
$ElementsInLog2 = (Get-ChildItem C:\temp\LogFolder\Log2\*.txt -Recurse).Length
$ElementsInLog3 = (Get-ChildItem C:\temp\LogFolder\Log3\*.txt -Recurse).Length

Write-Output "Main: $ElementsInLog`nLog1: $ElementsInLog1`nLog2: $elementsInLog2`nLog3: $elementsInLog3"
Write-output "Total File Size: $((Get-ChildItem C:\temp\LogFolder\Log1\*.txt -Recurse | Measure-Object length -Sum).Sum / 1024 / 1024 / 1024) GB"


Write-Output "Starting Tasks..."

$job1 = Start-Job -ScriptBlock {
  Write-Output "Log1: $(Get-Date -Format G)"
  Get-ChildItem C:\temp\LogFolder\Log1 -Recurse | Compress-Archive -DestinationPath C:\temp\log1.zip -CompressionLevel Fastest
  Write-Output "Finished: $(Get-Date -Format G)"
}

$job2 = Start-Job -ScriptBlock {
    Write-Output "Log2: $(Get-Date -Format G)"
    Get-ChildItem C:\temp\LogFolder\Log3 -Recurse | Compress-Archive -DestinationPath C:\temp\log3.zip -CompressionLevel Fastest
    Write-Output "Finished: $(Get-Date -Format G)"
}

$job3 = Start-Job -ScriptBlock {
    Write-Output "Log2: $(Get-Date -Format G)"
    Get-ChildItem C:\temp\LogFolder\Log4 | Compress-Archive -DestinationPath C:\temp\log4.zip -CompressionLevel Fastest
    Write-Output "Finished: $(Get-Date -Format G)"
}

while($job1.State -eq "Running" -or $job2.State -eq "Running" -or $job3.State -eq "Running") { 
    Start-Sleep 5
}

Receive-Job $job1
Receive-Job $job2
Receive-Job $job3


Вывод получен

Main: 8000
Log1: 2000
Log2: 2000
Log3: 2000
Total File Size: 4.12791967391968 GB

Starting Tasks...
Log1: 2/10/2020 8:36:22 PM
Finished: 2/10/2020 8:37:30 PM
Log2: 2/10/2020 8:36:22 PM
Finished: 2/10/2020 8:37:27 PM
Log3: 2/10/2020 8:36:22 PM
Finished: 2/10/2020 8:37:28 PM
person Jawad    schedule 11.02.2020
comment
Спасибо. К сожалению, как я уже сказал, я хочу использовать Winrar, потому что я хочу, чтобы дата архива совпадала с датой файла, и я хочу удалить файлы после архивирования. Я также хочу, чтобы это было достаточно умно, чтобы не удалять файлы, которые не удалось заархивировать. Я не вижу этой логики выше. (В Winrar это встроено). Прежде чем публиковать здесь, я смог создать двухстрочную команду для сжатия файлов с помощью встроенной функции сжатия-архивирования. Он отлично работает, за исключением тех двух требований, которые заставили меня опубликовать здесь. Ответ Мэтью - это то, что мне нужно. - person ecksfiftyone; 13.02.2020
comment
Вы написали: Я не женат на Winrar, если я могу добиться этого другим способом, поэтому предположил, что вы попробуете то, что встроено в powershell. Но если вы нашли способ с помощью winrar, и производительность и сжатие — это то, что вам нужно, используйте этот вариант. - person Jawad; 13.02.2020
comment
Извините, если я не был ясен. Я должен был лучше сформулировать свой первоначальный запрос. Правильно, я не женат на WinRar, ЕСЛИ я могу добиться этого другим способом, который ссылался на два пункта, непосредственно предшествующих этому. (Установите дату и время архива так, чтобы они соответствовали дате/времени заархивированного файла, а также удалите исходный файл, ТОЛЬКО если заархивирование действительно успешно) Насколько я могу судить, ни один из них не был удовлетворен этим сценарием. С Compress-Archive все в порядке, если скрипт обрабатывает эти два других требования. - person ecksfiftyone; 15.02.2020