Cómo sincronizar dos carpetas en PowerShell: paso a paso
Aprende a sincronizar dos carpetas en PowerShell para mantener una copia de seguridad coherente y reducir transferencias innecesarias. Esta guía muestra cómo comparar, copiar solo archivos nuevos o modificados y registrar operaciones, útil para copias locales y migraciones sencillas.
Requisitos previos
- PowerShell 5.1 o PowerShell 7+ instalado.
- Permisos de lectura en la carpeta de origen y de escritura en la carpeta de destino.
- Espacio suficiente en disco en la carpeta de destino.
Paso 1: Estructura básica y variables
Empieza por definir rutas y opciones. Usa una variable $WhatIf para ejecutar un dry-run sin copiar archivos — muy útil para probar.
$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
}
Paso 2: Script para sincronizar dos carpetas (copia incremental)
El principio es simple: enumerar archivos en $Source, calcular la ruta relativa y comparar con el archivo correspondiente en $Destination. Se copia solo cuando el archivo no existe o ha sido modificado (tamaño o fecha).
$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 }
}
}
Paso 3: Registro y manejo de errores
Registrar operaciones ayuda a auditar el proceso. Añade Try/Catch para capturar fallos y escribe entradas en el archivo de registro.
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
}
}
Paso 4: Opcional — eliminar archivos que han desaparecido en el origen
Si quieres que el destino refleje exactamente el origen (sincronización bidireccional no es este ejemplo), elimina archivos en el destino que ya no existen en el origen. Haz siempre un dry-run primero.
# Cuidado: esta acción borra archivos en el 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 }
}
}
Verificar el resultado
Para confirmar que la sincronización funcionó: 1) ejecuta el script con $WhatIf = $true y verifica los mensajes; 2) ejecuta con $WhatIf = $false en un pequeño conjunto de archivos; 3) consulta el $LogFile para registros de copias/errores; 4) valida tamaños y fechas entre $Source y $Destination con Get-ChildItem.
Conclusión
Sincronizar dos carpetas en PowerShell es una solución simple y controlable para copias locales y migraciones. A partir de este esqueleto puedes añadir filtros por extensión, paralelismo con Start-Job o integración con herramientas de compresión. Consejo: prueba siempre con $WhatIf = $true antes de borrar archivos en producción — ¿prefieres conservar versiones antiguas o un historial de copias?