Un script híbrido (Batch + PowerShell) que hace copia completa, comprime en ZIP por fecha, elimina backups antiguos, registra todo y calcula tiempos. Ideal para pymes, autónomos y despachos que quieren protección local sin montar infra compleja.
¿Por qué Robocopy?
- Viene en Windows, es muy fiable, entiende NTFS (atributos, permisos) y tiene modos resilientes para enlaces, reintentos, etc.
- Es perfecto para copias diarias a disco local/externo/NAS sin quebraderos de cabeza.
- Con un poco de PowerShell, añadimos ZIP, limpieza por retención, hash opcional y logs legibles.
Qué hace este flujo
- Copia completa desde origen a carpeta de trabajo del día (timestamp) usando Robocopy.
- Comprime la copia del día en un
.zip
(uno por día). - Limpieza de backups antiguos (tanto carpetas como ZIP) según retención configurable.
- Logs detallados (+ evento en el Visor de sucesos si quieres).
- Cálculo de duración y código de salida claro para integrarlo con monitorización.
- Ejecución manual o automática con el Programador de tareas.
Mejores prácticas aplicadas
- Rendimiento:
/MT:16
(multihilo),/R:3 /W:5
(reintentos moderados),/Z
(reinicia transferencias),/FFT
(tolerancia timestamps FAT/NAS),/COPY:DAT
(datos, atributos y timestamps). - Calidad de datos:
/DCOPY:T
para directorios,/XJ
para evitar bucles con junctions típicas de perfiles. - Control y seguridad: exclusiones (
/XD
,/XF
), logs separados diarios, y opción de hash (SHA256) para validar que el ZIP no se corrompió. - Rotación simple por días, evitando “crecimiento infinito”.
Nota: si necesitas copiar archivos bloqueados (bases de datos, PST abiertos, etc.), plantéate usar VSS (instantánea) y copiar desde el snapshot. Podemos derivarlo a un
.dsh
condiskshadow
o awbadmin
. Para la mayoría de oficinas y documentos ofimáticos, la ventana fuera de horario + Robocopy suele bastar.
Script híbrido mejorado
- Configuración al principio (rutas y retención).
- Registro en
logFolder\backup-YYYYMMDD.log
. - ZIP en
zipFolder\Backup-YYYYMMDD.zip
. - Limpieza de ficheros/carpetas con más de
diasRetencion
.
Copia, pega y ajusta rutas. Guarda como robocopy_backups.bat
.
@echo off
setlocal enableextensions enabledelayedexpansion
REM ======== CONFIGURACION ========
set "origen=C:\Datos"
set "baseDestino=D:\Backups\Diario"
set "zipFolder=D:\Backups\Zip"
set "logFolder=D:\Backups\Logs"
set "diasRetencion=14"
REM (Opcional) Exclusiones: carpetas y archivos
set "EXCL_DIRS=System Volume Information $Recycle.Bin node_modules .git .cache"
set "EXCL_FILES=Thumbs.db *.tmp *.log~"
REM (Opcional) Hash del ZIP (1=si, 0=no)
set "GENERAR_HASH=1"
REM ======== PREPARACION ========
for /f "tokens=1-3 delims=/-. " %%a in ('wmic os get localdatetime ^| find "."') do set "YYYYMMDD=%%a" & set "HHMMSS=%%b"
set "YYYY=%YYYYMMDD:~0,4%"
set "MM=%YYYYMMDD:~4,2%"
set "DD=%YYYYMMDD:~6,2%"
set "HOY=%YYYY%%MM%%DD%"
set "destino=%baseDestino%\%HOY%"
set "logFile=%logFolder%\backup-%HOY%.log"
set "zipFile=%zipFolder%\Backup-%HOY%.zip"
set "hashFile=%zipFolder%\Backup-%HOY%.sha256.txt"
for %%D in ("%baseDestino%" "%zipFolder%" "%logFolder%") do if not exist "%%~D" mkdir "%%~D"
if not exist "%destino%" mkdir "%destino%"
echo ================================================== >> "%logFile%"
echo Inicio: %date% %time% >> "%logFile%"
echo Origen: %origen% >> "%logFile%"
echo Destino: %destino% >> "%logFile%"
REM ======== CONSTRUIR PARAMETROS DE EXCLUSION ========
set "EXCLUDE_PARAMS="
for %%X in (%EXCL_DIRS%) do set "EXCLUDE_PARAMS=!EXCLUDE_PARAMS! /XD ""%%~X"""
for %%X in (%EXCL_FILES%) do set "EXCLUDE_PARAMS=!EXCLUDE_PARAMS! /XF ""%%~X"""
REM ======== COPIA CON ROBOCOPY ========
REM /E copia subcarpetas incluidas vacías; /COPY:DAT datos, atributos y tiempo
REM /DCOPY:T conserva timestamps de directorios; /Z reiniciable; /R:3 reintentos; /W:5 espera
REM /MT:16 multihilo; /XJ evita bucles por junctions; /FFT tolerancia timestamps (NAS)
set "ROBO_OPTS=/E /COPY:DAT /DCOPY:T /Z /R:3 /W:5 /MT:16 /XJ /FFT /NP /NFL /NDL"
echo Ejecutando Robocopy... >> "%logFile%"
robocopy "%origen%" "%destino%" %ROBO_OPTS% %EXCLUDE_PARAMS% /TEE /LOG+:"%logFile%"
set "RC=%ERRORLEVEL%"
REM Códigos de Robocopy: 0,1 = OK; >=8 = error.
if %RC% GEQ 8 (
echo [ERROR] Robocopy devolvio %RC%. >> "%logFile%"
goto :fin
) else (
echo Robocopy finalizado con codigo %RC%. >> "%logFile%"
)
REM ======== COMPRESION ZIP (PowerShell) ========
echo Comprimiendo a ZIP: "%zipFile%" >> "%logFile%"
powershell -NoProfile -Command ^
"Try {
Add-Type -AssemblyName 'System.IO.Compression.FileSystem';
if (Test-Path -LiteralPath '%zipFile%'){ Remove-Item -LiteralPath '%zipFile%' -Force }
[System.IO.Compression.ZipFile]::CreateFromDirectory('%destino%', '%zipFile%');
'ZIP creado correctamente' | Out-File -FilePath '%logFile%' -Append -Encoding utf8
} Catch {
'ERROR ZIP: ' + $_.Exception.Message | Out-File -FilePath '%logFile%' -Append -Encoding utf8; exit 1 }"
if errorlevel 1 (
echo [ERROR] Fallo al crear ZIP. >> "%logFile%"
goto :fin
)
REM ======== HASH (opcional) ========
if "%GENERAR_HASH%"=="1" (
echo Generando SHA256... >> "%logFile%"
powershell -NoProfile -Command ^
"$h=Get-FileHash -LiteralPath '%zipFile%' -Algorithm SHA256; $h.Hash + ' ' + (Split-Path '%zipFile%' -Leaf) | Out-File -FilePath '%hashFile%' -Encoding ascii; 'SHA256 listo' | Out-File -FilePath '%logFile%' -Append -Encoding utf8"
)
REM ======== LIMPIEZA POR RETENCION ========
echo Limpiando elementos con mas de %diasRetencion% dias... >> "%logFile%"
powershell -NoProfile -Command ^
"Try {
$limit=(Get-Date).AddDays(-%diasRetencion%);
Get-ChildItem -LiteralPath '%baseDestino%' -Directory | Where-Object { $_.LastWriteTime -lt $limit } | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue;
Get-ChildItem -LiteralPath '%zipFolder%' -File | Where-Object { $_.LastWriteTime -lt $limit } | Remove-Item -Force -ErrorAction SilentlyContinue;
'Limpieza completada' | Out-File -FilePath '%logFile%' -Append -Encoding utf8
} Catch {
'ERROR LIMPIEZA: ' + $_.Exception.Message | Out-File -FilePath '%logFile%' -Append -Encoding utf8 }"
REM ======== FIN OK ========
:fin
echo Fin: %date% %time% >> "%logFile%"
REM Registra evento en el Visor (opcional)
REM eventcreate /T INFORMATION /ID 1000 /L APPLICATION /SO RobocopyBackup /D "Backup %HOY% finalizado (RC=%RC%)"
exit /b %RC%
Lenguaje del código: PHP (php)
Consejo: prueba primero con
/L
(modo simulación) para ver qué copiaría Robocopy sin tocar nada.
Programarlo en el Programador de tareas
- Abre Programador de tareas → Crear tarea…
- Desencadenadores: diario a las 20:00 (o tras el cierre).
- Acciones:
- Programa:
C:\Windows\System32\cmd.exe
- Argumentos:
/c "D:\Backups\robocopy_backups.bat"
- Programa:
- Condiciones: desmarca “Detener si pasa a batería” si es portátil y te interesa que corra igual.
- Configuración: marca “Detener si se ejecuta más de X horas” (p.e. 6 h) y “Si la tarea ya se está ejecutando, no iniciar una instancia nueva”.
Si lo prefieres, puedo darte un
.xml
listo para importar con un horario concreto.
Restauración (el día que toque)
- Localiza el ZIP del día (o la carpeta del día si conservas copias sin comprimir).
- Verifica el hash (si generas
.sha256
):- PowerShell:
Get-FileHash .\Backup-YYYYMMDD.zip -Algorithm SHA256
y compara con el TXT.
- PowerShell:
- Descomprime en una carpeta de staging y revisa estructura y permisos.
- Copia de vuelta lo necesario (o usa Robocopy
/MIR
con cuidado si quieres espejo).
Exclusiones útiles (evita ruido y ciclos)
- Carpetas:
.git
,node_modules
,.cache
,AppData\Local\Temp
,System Volume Information
,$Recycle.Bin
. - Ficheros:
Thumbs.db
,*.tmp
,*.log~
,*.etl
. - Evitas copiar basura, temporales y junctions que pueden multiplicar volumen o hacer bucles.
Seguridad y cumplimiento
- Cifrado:
Compress-Archive
/Zip .NET no encripta. Si necesitas cifrado AES-256, usa 7-Zip (CLI) y protege con contraseña (gestión segura). - Ransomware: los backups locales pueden verse comprometidos si la máquina se infecta. Valora un disco externo rotativo (conectar, hacer copia, desconectar) o un destino NAS con cuentas separadas y versión inmutable si es posible.
- Privilegios: si necesitas copiar con privilegios de backup, ejecuta la tarea con una cuenta que tenga el privilegio “Back up files and directories” y usa
/B
. - VSS (archivos en uso): para snapshots consistentes de bases de datos, combina con
diskshadow
o procesos específicos de tu software contable/ERP.
Rendimiento
- Hilo múltiple
/MT:16
es un buen equilibrio en HDD/NAS. En SSD/NVMe y red rápida puedes subir a 32 o 64. - NAS/SMB: añade
/IPG:10
si saturas la red en horas punta. - Incrementales reales: Robocopy ya omite lo igual, pero si quieres espejo 1:1 usa
/MIR
(con cautela: también borra en destino lo que se borra en origen).
Monitorización y alertas
- Los códigos de salida de Robocopy son muy informativos:
0/1 OK
,>= 8
error. - Puedes enviar correo en caso de error con PowerShell (requiere un SMTP confiable).
- O lanzar un evento de Windows (ya te dejé
eventcreate
comentado).
Casos de uso reales
- Autónomos: respaldo diario en un USB 3.0 rotativo (L-V), zip con fecha y 30 días de retención.
- Gestorías: copia cada noche a NAS + zip, con exclusiones de temporales y verificación por hash; VSS semanal para ficheros en uso.
- Micropymes: backup local + sincronización diferencial semanal a un NAS en otra planta.
Preguntas frecuentes
¿Sirve para “incrementales”?
Robocopy omite lo que no ha cambiado; en la práctica, verás que sólo copia lo nuevo/modificado. Si necesitas versiones históricas por archivo, entonces evalúa Historial de archivos o un versionado en NAS.
¿Y si mi ZIP supera 4 GB?
El ZIP de .NET soporta >4 GB. Si necesitas partir en trozos, usa 7-Zip (7z a -v2g ...
) y conserva índices.
¿Cómo lo paro si se cuelga?
Configura en el Programador el tiempo máximo, y añade /TBD
(espera unidades) sólo si te hace falta. En red inestable, reduce /R
y /W
.
¿Se puede subir encriptado a la nube?
Sí. Cifra con 7-Zip o un contenedor (p.ej. VeraCrypt) y sincroniza con tu herramienta de confianza. Cuidado con costes y RGPD.
Código fuente
El repositorio original que mencionas:
GitHub: https://github.com/albertlopezespinosa/Robocopy