How to synchronize two folders in PowerShell: step by step
Learn how to synchronize two folders in PowerShell to maintain a consistent backup and reduce unnecessary transfers. This guide shows how to compare, copy only new or changed files and log operations, useful for local backups and simple migrations.
Prerequisites
- PowerShell 5.1 or PowerShell 7+ installed.
- Read permissions on the source folder and write permissions on the destination folder.
- Sufficient free disk space on the destination folder.
Step 1: Basic structure and variables
Start by defining paths and options. Use a variable $WhatIf to run a dry-run without copying files — very useful for testing.
$Source = 'C:\Fonte'
$Destination = 'D:\Backup'
$LogFile = 'C:\Backup\sync.log'
$WhatIf = $true # Mudar para $false para executar a cópia
# Garantir que a pasta destino existe
if (-not (Test-Path $Destination)) {
New-Item -ItemType Directory -Path $Destination -Force | Out-Null
}
Step 2: Script to synchronize two folders (incremental copy)
The principle is simple: enumerate files in $Source, compute the relative path and compare with the corresponding file in $Destination. Copy only when the file does not exist or has changed (size or date).
$sourceFiles = Get-ChildItem -Path $Source -File -Recurse
foreach ($s in $sourceFiles) {
$rel = $s.FullName.Substring($Source.Length).TrimStart('\')
$destPath = Join-Path $Destination $rel
$destDir = Split-Path $destPath -Parent
if (-not (Test-Path $destDir)) {
if ($WhatIf) { Write-Output "Would create folder: $destDir" }
else { New-Item -ItemType Directory -Path $destDir -Force | Out-Null }
}
$copy = $true
if (Test-Path $destPath) {
$d = Get-Item $destPath
if ($s.Length -eq $d.Length -and $s.LastWriteTime -le $d.LastWriteTime) { $copy = $false }
}
if ($copy) {
if ($WhatIf) { Write-Output "Would copy: $rel" }
else { Copy-Item -Path $s.FullName -Destination $destPath -Force }
}
}
Step 3: Logging and error handling
Logging operations helps audit the process. Add Try/Catch to capture failures and write entries to the log file.
foreach ($s in $sourceFiles) {
$rel = $s.FullName.Substring($Source.Length).TrimStart('\')
$destPath = Join-Path $Destination $rel
$destDir = Split-Path $destPath -Parent
try {
if (-not (Test-Path $destDir)) {
if ($WhatIf) { Write-Output "Would create folder: $destDir" }
else { New-Item -ItemType Directory -Path $destDir -Force | Out-Null }
}
$copy = $true
if (Test-Path $destPath) {
$d = Get-Item $destPath
if ($s.Length -eq $d.Length -and $s.LastWriteTime -le $d.LastWriteTime) { $copy = $false }
}
if ($copy) {
if ($WhatIf) { Write-Output "Would copy: $rel" }
else {
Copy-Item -Path $s.FullName -Destination $destPath -Force
$entry = "{0} COPIED {1}" -f (Get-Date -Format s), $rel
Add-Content -Path $LogFile -Value $entry
}
}
}
catch {
$err = "{0} ERROR {1} - $_" -f (Get-Date -Format s), $rel
Add-Content -Path $LogFile -Value $err
}
}
Step 4: Optional — remove files that disappeared from the source
If you want the destination to exactly mirror the source (this example is not a two-way sync), remove files in the destination that no longer exist in the source. Always do a dry-run first.
# Cuidado: esta ação apaga ficheiros no destino
$destFiles = Get-ChildItem -Path $Destination -File -Recurse
foreach ($d in $destFiles) {
$rel = $d.FullName.Substring($Destination.Length).TrimStart('\')
$sourcePath = Join-Path $Source $rel
if (-not (Test-Path $sourcePath)) {
if ($WhatIf) { Write-Output "Would remove: $rel" }
else { Remove-Item -Path $d.FullName -Force }
}
}
Verify the result
To confirm the synchronization worked: 1) run the script with $WhatIf = $true and check the messages; 2) run with $WhatIf = $false on a small set of files; 3) check the $LogFile for copy/error records; 4) validate sizes and dates between $Source and $Destination with Get-ChildItem.
Conclusion
Synchronizing two folders in PowerShell is a simple, controllable solution for local backups and migrations. From this skeleton you can add filters by extension, parallelism with Start-Job or integration with compression tools. Tip: always test with $WhatIf = $true before deleting files in production — do you prefer to keep old versions or a backup history?