Надійне копіювання папок у Windows: PowerShell + Robocopy
Копіювання папок у Windows здається простою задачею рівно до моменту, коли потрібно зробити це регулярно, не втратити вкладені файли, побачити помилку мережі й мати журнал виконання. Для одного файлу достатньо Copy-Item. Для великої папки, NAS або резервної копії практичніше використати robocopy, а PowerShell залишити для параметрів, перевірок і зрозумілого запуску.
Нижче – готовий скрипт для Windows 10/11. Він копіює лише потрібні зміни, створює папку призначення, пише лог, вміє працювати в режимі перевірки та коректно розпізнає коди завершення robocopy.
Що робить скрипт
- перевіряє, що вихідна папка існує;
- створює папку призначення, якщо її ще немає;
- не дозволяє випадково копіювати папку саму в себе або у власну вкладену папку;
- копіює вкладені папки й файли з повторними спробами при тимчасовій помилці мережі;
- створює окремий файл логу в каталозі
logsпоруч зі скриптом; - підтримує
-DryRun: показує план операції без запису на диск; - має окрему небезпечну опцію
-Mirrorдля дзеркальної синхронізації.
Режим
-Mirrorвидаляє у папці призначення файли, яких уже немає в джерелі. Не використовуйте його для першого запуску або для єдиної копії даних.
Скрипт Copy-Folder.ps1
Створіть файл Copy-Folder.ps1 і вставте код нижче.
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$Source,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$Destination,
# Увімкнення /MIR: видаляє у Destination те, чого немає в Source.
[switch]$Mirror,
# Показує дії Robocopy, але нічого не копіює.
[switch]$DryRun
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
try {
if (-not (Test-Path -LiteralPath $Source -PathType Container)) {
throw "Вихідну папку не знайдено: $Source"
}
$sourcePath = (Resolve-Path -LiteralPath $Source).Path
if (-not (Test-Path -LiteralPath $Destination -PathType Container)) {
New-Item -ItemType Directory -Path $Destination -Force | Out-Null
}
$destinationPath = (Resolve-Path -LiteralPath $Destination).Path
if ($sourcePath.TrimEnd('\') -eq $destinationPath.TrimEnd('\')) {
throw 'Source і Destination не можуть бути однією папкою.'
}
$sourceWithSeparator = $sourcePath.TrimEnd('\') + '\'
if ($destinationPath.StartsWith(
$sourceWithSeparator,
[System.StringComparison]::OrdinalIgnoreCase
)) {
throw 'Destination не може бути вкладеною папкою Source.'
}
$logDirectory = Join-Path -Path $PSScriptRoot -ChildPath 'logs'
New-Item -ItemType Directory -Path $logDirectory -Force | Out-Null
$timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
$logFile = Join-Path -Path $logDirectory -ChildPath "copy-$timestamp.log"
$robocopyArgs = @(
$sourcePath
$destinationPath
'/E' # Усі підпапки, включно з порожніми.
'/COPY:DAT' # Дані, атрибути та часові мітки.
'/DCOPY:DAT'
'/Z' # Відновлюване копіювання при розриві мережі.
'/FFT' # Зручніше для NAS/FAT із грубішою точністю часу.
'/XJ' # Не проходити junction points.
'/R:2' # Не зависати: дві повторні спроби.
'/W:3' # Пауза між спробами - 3 секунди.
'/NP' # Не засмічувати лог відсотками.
'/TEE' # Показувати результат і в консолі, і в логу.
"/LOG:$logFile"
)
if ($DryRun) {
$robocopyArgs += '/L'
Write-Host 'Режим перевірки: файли не будуть скопійовані.' -ForegroundColor Yellow
}
if ($Mirror) {
$robocopyArgs += '/MIR'
Write-Warning 'Увімкнено MIRROR: зайві файли в Destination буде видалено.'
}
Write-Host "Джерело: $sourcePath"
Write-Host "Призначення: $destinationPath"
Write-Host "Лог: $logFile"
& robocopy @robocopyArgs
$robocopyExitCode = $LASTEXITCODE
# Для Robocopy коди 0-7 не означають фатальну помилку.
if ($robocopyExitCode -ge 8) {
throw "Robocopy завершився з помилкою. Код: $robocopyExitCode. Перевірте лог: $logFile"
}
Write-Host "Готово. Код Robocopy: $robocopyExitCode" -ForegroundColor Green
exit 0
}
catch {
Write-Error $_.Exception.Message
exit 1
}
Як запускати
Відкрийте Windows PowerShell або сучасний PowerShell 7, перейдіть до папки зі скриптом і спочатку запустіть перевірку. Вона сформує лог і покаже, що саме потрібно скопіювати, але не змінить файли.
cd C:\Scripts
.\Copy-Folder.ps1 -Source "C:\Users\ivan\Documents" -Destination "D:\Backup\Documents" -DryRun
Якщо результат правильний – повторіть команду без -DryRun:
.\Copy-Folder.ps1 -Source "C:\Users\ivan\Documents" -Destination "D:\Backup\Documents"
Для мережевої папки використовується UNC-шлях. Користувач, від якого запускається скрипт, має вже мати доступ до цієї шари:
.\Copy-Folder.ps1 -Source "C:\Work" -Destination "\\nas01\backup\Work"
Дзеркальна синхронізація
Якщо потрібно, щоб призначення стало точною копією джерела, додайте -Mirror. Це еквівалент параметра /MIR у Robocopy: він не лише додає та оновлює файли, а й видаляє зайве у папці призначення.
.\Copy-Folder.ps1 -Source "C:\Project" -Destination "D:\Mirror\Project" -Mirror -DryRun
Після перевірки заберіть -DryRun. Для домашніх документів або єдиного резервного диска зазвичай краще не використовувати дзеркало: резервна копія має захищати і від випадкового видалення в оригіналі.
Що з Execution Policy
Не потрібно вимикати захист PowerShell для всього комп’ютера. Спочатку подивіться, які політики діють:
Get-ExecutionPolicy -List
Для власного локального скрипта зручний обережний варіант на одну сесію:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned
Налаштування Process зникає після закриття вікна PowerShell. Якщо файл був завантажений з інтернету, але ви його перевірили й довіряєте йому, зніміть позначку завантаження окремою командою:
Unblock-File -LiteralPath .\Copy-Folder.ps1
Copy-Item: коли його достатньо
Для одного файлу або разової простої операції стандартного cmdlet цілком вистачає. Він підтримує -WhatIf, тобто режим без виконання.
Copy-Item -LiteralPath "C:\Temp\report.pdf" -Destination "D:\Archive\report.pdf" -Force -WhatIf
Для регулярного копіювання великих каталогів, мережевих шляхів і логування краще лишитися з варіантом на Robocopy вище. Він штатно є у Windows 10/11 і повертає коди 0-7 навіть у випадках, які не є фатальними помилками; скрипт враховує це правильно.
Практичні правила
- Спочатку завжди використовуйте
-DryRun. - Не зберігайте пароль до NAS або Windows-шари прямо у
.ps1. - Зберігайте логи: вони відповідають на запитання «що реально скопіювалося?».
- Перевіряйте відновлення файлів, а не лише факт виконання резервної копії.
- Для автоматичного запуску за розкладом використовуйте Планувальник завдань Windows від окремого облікового запису з мінімально потрібними правами.