Windows目录备份与恢复:Robocopy
适用环境:Windows Server 2019/2022/2025;使用系统自带 Robocopy,示例在 PowerShell 中执行。仅备份普通静态文件,不用于复制运行中数据库或制作系统裸机镜像。
示例源目录 C:\Sites\Example,目标 E:\Backups。E盘必须是实际存在、空间充足的目标;同一台服务器内不同盘符不一定是不同物理存储。远程桌面“把文件留在桌面”也不能算异地备份。所有分段命令在同一个PowerShell会话执行;重开窗口后,要先按记录重新设置 sourceDir、runDir 等变量。
修改路径后,先核对源目录、实际备份数据目录(本例runDir下的data)和试恢复目录的完整路径:三者不得相同或互相包含。目标落在源内可能把备份递归复制进自身;发现这类路径关系或无法确认时立即停止,不执行复制。runDir位于backupRoot内是正常的批次设计,不属于要禁止的包含关系。创建批次前和试恢复前都应按最终路径再次核对,不能只比较盘符。
准备独立批次,先预演
$sourceDir = 'C:\Sites\Example'
$backupRoot = 'E:\Backups'
if (-not (Test-Path -LiteralPath $sourceDir -PathType Container)) { throw '源目录不存在' }
$runDir = Join-Path $backupRoot ('files-' + (Get-Date -Format 'yyyyMMdd-HHmmss'))
New-Item -ItemType Directory -Path $runDir -ErrorAction Stop | Out-Null
$copyLog = Join-Path $runDir 'robocopy.log'
robocopy $sourceDir (Join-Path $runDir 'data') /E /COPY:DAT /DCOPY:DAT /XJ /R:2 /W:3 /L
/L 是仅列出计划,不实际复制。先停止并核对实际源、目标、计划文件、数量及错误;确认路径和清单都符合本次备份范围后,才单独执行下一段正式复制。有任何错误或不明路径,先修正,不继续。/E 包含空目录,/COPY:DAT 复制文件数据、属性和时间;这不是完整复制 NTFS 所有者、ACL或审计信息。/XJ 排除连接点,业务若依赖这些路径,应把对应数据另行列入计划。本示例不使用 /MIR、/PURGE、/MOVE,避免将误删同步到备份或删除源文件。
正式复制,并按退出码处理
robocopy $sourceDir (Join-Path $runDir 'data') /E /COPY:DAT /DCOPY:DAT /XJ /R:2 /W:3 /LOG:$copyLog
$copyStatus = $LASTEXITCODE
Get-Content -LiteralPath $copyLog -Tail 25
if ($copyStatus -ge 8) { throw "Robocopy失败,退出码=$copyStatus" }
"Robocopy退出码=$copyStatus,继续检查日志摘要与文件内容"
Robocopy 不是“非零全部失败”:0–7代表不同的复制、额外文件或差异组合,8及以上表示至少有复制失败。即使小于8,也要看 Failed、Mismatch、Skipped 等摘要是否符合预期。发生文件占用或访问拒绝,先安排应用支持的一致性备份窗口,不用无限重试掩盖问题。
恢复到新目录,不覆盖原站
$restoreDir = Join-Path 'C:\RestoreCheck' ('files-' + (Get-Date -Format 'yyyyMMdd-HHmmss'))
New-Item -ItemType Directory -Path $restoreDir -ErrorAction Stop | Out-Null
robocopy (Join-Path $runDir 'data') $restoreDir /E /COPY:DAT /DCOPY:DAT /XJ /R:2 /W:3
$restoreStatus = $LASTEXITCODE
if ($restoreStatus -ge 8) { throw "试恢复失败,退出码=$restoreStatus" }
选择几份已知文件,包括中文或带空格文件名、最新附件和较大的文件。先把下面sampleRelativePath替换为本次确认存在的同一个相对文件名或路径,不填绝对路径;保留前述同一PowerShell会话的sourceDir、runDir和restoreDir变量。分别读取源、备份data与试恢复目录下的同一份文件:
$sampleRelativePath = '替换为同一个已知文件的相对路径'
Get-FileHash -LiteralPath (Join-Path $sourceDir $sampleRelativePath) -Algorithm SHA256 -ErrorAction Stop | Format-List Algorithm,Hash,Path
Get-FileHash -LiteralPath (Join-Path (Join-Path $runDir 'data') $sampleRelativePath) -Algorithm SHA256 -ErrorAction Stop | Format-List Algorithm,Hash,Path
Get-FileHash -LiteralPath (Join-Path $restoreDir $sampleRelativePath) -Algorithm SHA256 -ErrorAction Stop | Format-List Algorithm,Hash,Path
三条命令都应成功,显示各自预期路径、SHA256算法和相同的Hash值;任一文件不存在、读取失败或哈希不同,都停止验收,先核对路径、复制日志及源文件是否在备份后发生变化,不继续覆盖原站。对多个样本重复此检查;样本一致也不能代替全部业务验收。确认内容后,再核对业务依赖的权限、软件版本、数据库和配置。这里复制的不是VSS应用一致性快照,不可据此认定运行中的数据库已完整备份。
完成后保留哪些信息
- 源路径、目标批次、运行时间、Robocopy日志和退出码。
- 备份目录与正式目录的对应关系,哪些连接点、占用文件或权限项目不在本例范围。
- 已通过的恢复样本、校验结果和恢复耗时。
- 一份服务器之外可读取的副本。恢复脚本应先在新目录验证,不能把昨天目录直接镜像回今天的业务目录。