У администратора накапливаются задачи, в которых руками работать уже невозможно. Заархивировать папки полусотни сотрудников по отдельности, упаковать логи десятков служб каждый в свой архив, собрать в архивы содержимое множества проектных каталогов. Кликать по каждой папке, выбирать упаковать, ждать, переходить к следующей, и так сотню раз, это работа на целый день монотонного труда, отупляющего и чреватого ошибками. А ведь та же сотня папок упаковывается одним прогоном скрипта за минуты, единообразно и без устали. Пакетное архивирование по списку и есть тот рубеж, за которым ручная упаковка уступает место автоматизации, освобождающей человека от бессмысленной рутины.

Идея пакетного архивирования проста: вместо упаковки папок по одной описываешь, что и как упаковать, и поручаешь это скрипту, который проходит по всему набору сам. Список папок может быть задан явно, взят из файла, собран по маске из родительского каталога, получен любым удобным способом. Скрипт перебирает этот список и каждую папку упаковывает в свой архив по единому правилу. Понять, как строить такие пакетные операции, значит научиться поручать машине монотонную работу, которую человек делает медленно, с ошибками и с тоской, а скрипт быстро, единообразно и безупречно.

Базовый перебор папок с упаковкой каждой в свой архив

Основа пакетного архивирования это цикл, проходящий по папкам и упаковывающий каждую. Простейший случай это упаковать все папки внутри родительского каталога, каждую в отдельный архив по её имени:

Get-ChildItem "C:\Proekty" -Directory | ForEach-Object { Compress-Archive -Path $_.FullName -DestinationPath "C:\Arhivy\$($_.Name).zip" }

Здесь берутся все подпапки родительского каталога, и каждая упаковывается в архив, названный по имени папки. Сто папок обработаются так же легко, как одна, разница лишь во времени работы. Ключевая деталь тут это присвоение архиву имени по имени папки, отчего архивы получают осмысленные имена и не путаются. Без этого пришлось бы как-то иначе различать архивы, а так каждый сразу узнаётся по имени исходной папки.

Часто нужно упаковывать не все подпапки подряд, а только подходящие под условие. Фильтрация папок перед упаковкой задаётся условием в цепочке. Упаковка только папок, изменённых недавно, например, отбирает их по дате:

Get-ChildItem "C:\Dannye" -Directory | Where-Object {$_.LastWriteTime -gt (Get-Date).AddDays(-7)} | ForEach-Object { Compress-Archive -Path $_.FullName -DestinationPath "C:\Arhivy\$($_.Name).zip" -Force }

Здесь упаковываются лишь папки, менявшиеся за последнюю неделю, а нетронутые пропускаются. Такая фильтрация экономит силы, архивируя только то, что реально изменилось и нуждается в свежей копии. Условие отбора может быть любым: по дате, по размеру, по имени, по содержимому, и эта гибкость позволяет точно нацелить пакетную операцию на нужное подмножество папок, не трогая остального.

Архивирование по явному списку из файла

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

Get-Content "C:\Spiski\papki.txt" | ForEach-Object { $imya = Split-Path $_ -Leaf; Compress-Archive -Path $_ -DestinationPath "C:\Arhivy\$imya.zip" -Force }

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

Подход со списком из файла хорош и для документирования того, что архивируется. Файл со списком папок сам по себе служит наглядной картой охвата резервного копирования: открыл и видишь, что входит в архивирование, а что нет. Это прозрачнее, чем логика отбора, зашитая в скрипт, где охват надо вычитывать из кода. Для понятности и управляемости явный список нередко предпочтительнее хитрой автоматической выборки, особенно когда важно, чтобы охват архивирования был ясен и легко проверяем, а не выводился неочевидным условием.

Сохранение структуры и осмысленное именование архивов

При пакетном архивировании важно продумать, как назвать архивы и куда их сложить, иначе сотня архивов превратится в свалку. Осмысленное именование это основа порядка. Помимо имени папки в имя архива нередко добавляют дату, чтобы различать копии одной папки, сделанные в разное время. Архивирование с именем из папки и даты собирается так:

$data = Get-Date -Format "yyyy-MM-dd"
Get-ChildItem "C:\Otdely" -Directory | ForEach-Object { Compress-Archive -Path $_.FullName -DestinationPath "C:\Arhivy\$($_.Name)-$data.zip" -Force }

Здесь каждый архив получает имя из имени папки и текущей даты, отчего архивы разных дней не затирают друг друга и легко сортируются. Такое именование превращает набор архивов в упорядоченную летопись, где сразу видно, что и когда упаковано. Это куда лучше безликих имён, в которых потом не разберёшь, какой архив какой папке и какому дню принадлежит.

Полезно продумать и раскладку архивов по подпапкам, когда их очень много. Сложить сотни архивов в одну папку значит затруднить поиск нужного, тогда как раскладка по подпапкам, скажем по дате или по категории, сохраняет порядок. Скрипт может создавать подпапки и складывать архивы в них по логике, удобной для последующего поиска. Эта забота о структуре хранилища архивов окупается, когда понадобится найти конкретный архив среди множества, ведь в упорядоченном хранилище это секунды, а в свалке мучительный перебор.

Надёжность пакетной операции и обработка сбоев

Пакетное архивирование сотни папок неизбежно столкнётся со сбоями на отдельных папках, и без их обработки один сбой обрушит всю операцию. Папка может оказаться недоступной, занятой, слишком большой, и слепой скрипт споткнётся на ней, бросив архивирование остальных. Поэтому упаковку оборачивают в перехват ошибок, чтобы сбой на одной папке не прерывал обработку прочих. Каркас с перехватом ошибки внутри цикла делает операцию устойчивой:

Get-ChildItem "C:\Dannye" -Directory | ForEach-Object { try { Compress-Archive -Path $_.FullName -DestinationPath "C:\Arhivy\$($_.Name).zip" -Force -ErrorAction Stop } catch { Write-Warning "Не удалось: $($_.Name) - $_" } }

Здесь каждая папка упаковывается в защищённом блоке, и при сбое скрипт записывает предупреждение и идёт дальше, а не падает целиком. Так из сотни папок заархивируются все доступные, а проблемные будут отмечены для разбора, вместо того чтобы одна неудача остановила всю работу. Эта устойчивость превращает хрупкий скрипт в надёжный инструмент пакетной обработки, доводящий дело до конца на любом наборе папок.

Полезно вести и журнал пакетной операции, записывая, что заархивировано успешно, а что нет. Для сотни папок держать в голове, всё ли прошло гладко, невозможно, а журнал даёт полную картину результата. Запись итогов каждой папки в журнал превращает пакетную операцию в подотчётную: по журналу видно, сколько папок обработано, сколько сбоев, какие именно папки не удались и почему. Этот журнал бесценен и для проверки полноты архивирования, и для разбора сбоев, ведь без него результат массовой операции остаётся туманным, и неясно, всё ли упаковалось как надо.

Запуск по расписанию для регулярного пакетного архивирования

Пакетное архивирование раскрывает полную силу, когда его ставят на расписание, превращая в регулярную автоматическую операцию. Скрипт, упаковывающий нужные папки, назначают на ночной запуск через планировщик, и каждую ночь он сам создаёт свежие архивы, накапливая историю без всякого участия человека. Создание задания для регулярного пакетного архивирования делается командой планировщика:

schtasks /create /tn "ПакетноеАрхивирование" /tr "powershell -File C:\Scripts\arhiv-vse.ps1" /sc daily /st 02:00

Здесь скрипт пакетного архивирования назначается на ежедневный ночной запуск, когда машина свободна. Так монотонная работа, которую человек делал бы вручную полдня, уходит в ночь и делается сама, а наутро готовы свежие архивы. Это и есть высшая форма автоматизации архивирования: не просто скрипт вместо ручной упаковки, а полностью самостоятельная регулярная операция, не требующая внимания, пока работает исправно.

К такому регулярному архивированию обязательно добавляют контроль, чтобы молчаливый сбой не оставил без копий. Скрипт, отрабатывающий ночью, должен сигналить о проблемах, иначе отказ архивирования всплывёт лишь тогда, когда копия понадобится, а её нет. Поэтому регулярное пакетное архивирование снабжают и журналом, и оповещением о сбоях, чтобы проблему заметили вовремя. Эта связка автоматического выполнения и контроля результата и отличает надёжную систему резервного копирования от скрипта, который вроде работает, но втихую мог давно сломаться, оставив администратора без копий в нужный момент.

Контроль места и параллельная обработка для больших наборов

При пакетном архивировании больших наборов всплывают задачи, не возникающие при упаковке одной папки. Первая это контроль свободного места. Сотня архивов может занять немало, и если место на диске назначения кончится посреди операции, часть архивов не создастся, а скрипт наспотыкается на ошибках. Поэтому разумный скрипт проверяет свободное место перед началом и при нужде предупреждает. Проверка свободного места на диске назначения делается запросом:

(Get-PSDrive C).Free / 1GB

Зная свободное место и примерный объём архивируемого, скрипт оценивает, хватит ли места, и не начинает обречённую операцию, которая всё равно оборвётся на полпути. Эта предусмотрительность избавляет от ситуации, когда полночи архивирования пропадает зря из-за того, что место кончилось на середине, а проверить его заранее было делом одной команды.

Вторая задача больших наборов это скорость. Архивирование сотни папок по очереди может тянуться долго, ведь каждая упаковывается, пока предыдущая не закончена. Современные средства позволяют обрабатывать несколько папок одновременно, задействуя многоядерность машины, отчего пакетная операция ускоряется в разы. Параллельная обработка раскладывает упаковку папок на несколько одновременных потоков, и общее время сокращается тем сильнее, чем больше ядер у машины. Для очень больших наборов это превращает многочасовую операцию в гораздо более быструю.

Правда, у параллельности есть и цена, которую надо понимать. Одновременная упаковка нескольких папок нагружает и процессор, и диск сильнее последовательной, и на слабой машине или медленном диске выигрыш может оказаться меньше ожидаемого, а то и обернуться затором, когда потоки дерутся за один диск. Поэтому степень параллельности подбирают под возможности машины: на мощной с быстрым диском больше потоков, на слабой меньше или вовсе последовательно. Это размен между скоростью и нагрузкой, который решают под конкретное оборудование, а не гонятся за максимальной параллельностью вслепую.

Полезно понимать и то, что не всякое архивирование выигрывает от параллельности одинаково. Если узкое место это диск, а не процессор, одновременная упаковка нескольких папок упрётся в пропускную способность диска и не ускорится, как ждали. А вот при упаковке с сильным сжатием, нагружающим процессор, параллельность на многоядерной машине даёт заметный выигрыш. Понимание, где узкое место, диск или процессор, и подсказывает, стоит ли распараллеливать конкретное архивирование или последовательная обработка будет не хуже, избавляя от усложнения скрипта там, где оно не окупится.

Пакетное архивирование папок по списку освобождает от монотонной ручной упаковки, поручая машине то, что человек делает медленно, с ошибками и с тоской. Цикл проходит по набору папок, заданному маской, фильтром или явным списком, и упаковывает каждую в свой осмысленно названный архив, а перехват ошибок и журнал делают операцию надёжной и подотчётной на любом числе папок. Поставленное на расписание, такое архивирование становится самостоятельной регулярной операцией, накапливающей свежие копии без участия человека. Освоив построение пакетных операций, администратор перестаёт тратить часы на упаковку папок по одной и поручает эту работу скрипту, оставляя себе задачи, где действительно нужны человеческая мысль и решение, а не бессмысленное повторение одних и тех же кликов сотню раз подряд.