PowerShell跨服务器目录文件比对 免全量哈希及报错方案求助
双目录文件比对PowerShell优化方案
原有脚本的核心问题有三个:
- 直接将
Get-ChildItem返回的原生文件对象传入Compare-Object,没有提取相对路径作为统一比对键,既无法准确识别单侧缺失的条目,还会因为对象属性不匹配持续抛错 - 没有做阶段拆分,存在性校验和内容校验混在一起跑,逻辑混乱
- 没有利用文件系统元数据做初筛,直接全量算哈希的话10G文件要跑很久,完全没必要
第一阶段:存在性校验实现
核心逻辑是把两个路径下的所有文件、目录都转换为以相对路径为唯一标识的自定义对象,直接对比相对路径就能快速筛出单侧缺失的条目,不会触发异常。
# 配置两个目标路径 $FirstPath = "\\Server001\c$\Files" $SecondPath = "\\Server002\c$\Files" # 递归拉取两个路径下所有文件、目录,统一计算相对路径、基础元数据 $firstEntries = Get-ChildItem -Recurse -Path $FirstPath -File -Directory -ErrorAction SilentlyContinue | ForEach-Object { [PSCustomObject]@{ RelPath = $_.FullName.Substring($FirstPath.Length).TrimStart('\') IsDir = $_.PSIsContainer Length = if (-not $_.PSIsContainer) { $_.Length } else { $null } LastWriteTimeUtc = $_.LastWriteTimeUtc FullPath = $_.FullName } } $secondEntries = Get-ChildItem -Recurse -Path $SecondPath -File -Directory -ErrorAction SilentlyContinue | ForEach-Object { [PSCustomObject]@{ RelPath = $_.FullName.Substring($SecondPath.Length).TrimStart('\') IsDir = $_.PSIsContainer Length = if (-not $_.PSIsContainer) { $_.Length } else { $null } LastWriteTimeUtc = $_.LastWriteTimeUtc FullPath = $_.FullName } } # 筛选单侧存在的条目 $onlyInFirst = Compare-Object -ReferenceObject $firstEntries -DifferenceObject $secondEntries -Property RelPath -PassThru | Where-Object SideIndicator -eq '<=' $onlyInSecond = Compare-Object -ReferenceObject $firstEntries -DifferenceObject $secondEntries -Property RelPath -PassThru | Where-Object SideIndicator -eq '=>' # 输出存在性校验结果 if ($onlyInFirst -or $onlyInSecond) { Write-Host "`n=== 存在性校验结果:检出单侧缺失条目 ===" -ForegroundColor Red if ($onlyInFirst) { Write-Host "`n仅在第一个路径存在的条目:" $onlyInFirst | ForEach-Object { Write-Host "- $($_.RelPath) [$($_.IsDir ? '目录' : '文件')]" } } if ($onlyInSecond) { Write-Host "`n仅在第二个路径存在的条目:" $onlyInSecond | ForEach-Object { Write-Host "- $($_.RelPath) [$($_.IsDir ? '目录' : '文件')]" } } } else { Write-Host "`n=== 存在性校验结果:两侧目录结构完全匹配,无缺失条目 ===" -ForegroundColor Green }
第二阶段:低开销内容差异比对
不需要对所有文件计算哈希,文件系统自带的两个元数据和内容强相关:文件大小、UTC时间戳的最后修改时间。绝大多数篡改场景要么改了文件大小,要么会更新最后修改时间,先用这两个字段做初筛,只对元数据不匹配的可疑文件计算哈希做最终校验,10G高相似度目录下通常只需要计算极少量文件的哈希,速度提升非常明显。
# 提取两侧共有的文件(目录不需要比对内容) $commonFiles = $firstEntries | Where-Object { -not $_.IsDir } | ForEach-Object { $match = $secondEntries | Where-Object RelPath -eq $_.RelPath if ($match) { [PSCustomObject]@{ RelPath = $_.RelPath FirstLength = $_.Length SecondLength = $match.Length FirstWriteTime = $_.LastWriteTimeUtc SecondWriteTime = $match.LastWriteTimeUtc FirstFullPath = $_.FullPath SecondFullPath = $match.FullPath } } } # 初筛元数据不一致的可疑文件 $suspectFiles = $commonFiles | Where-Object { $_.FirstLength -ne $_.SecondLength -or $_.FirstWriteTime -ne $_.SecondWriteTime } if (-not $suspectFiles) { Write-Host "`n=== 内容校验结果:所有共有文件大小、修改时间完全匹配,无内容差异 ===" -ForegroundColor Green exit 0 } Write-Host "`n=== 内容校验结果:检出$($suspectFiles.Count)个元数据异常文件,启动哈希校验 ===" -ForegroundColor Yellow $diffFiles = @() foreach ($file in $suspectFiles) { # 仅对可疑文件计算MD5哈希,MD5用于文件篡改校验足够,速度比SHA256快30%以上 $hash1 = (Get-FileHash -Path $file.FirstFullPath -Algorithm MD5).Hash $hash2 = (Get-FileHash -Path $file.SecondFullPath -Algorithm MD5).Hash if ($hash1 -ne $hash2) { $diffFiles += [PSCustomObject]@{ RelPath = $file.RelPath FirstHash = $hash1 SecondHash = $hash2 } } } if ($diffFiles) { Write-Host "`n最终确认内容不一致的文件:" -ForegroundColor Red $diffFiles | ForEach-Object { Write-Host "- $($_.RelPath)" } } else { Write-Host "`n可疑文件哈希校验全部通过,无实际内容差异(元数据差异为属性修改导致,不影响文件内容)" -ForegroundColor Green }
脚本使用注意事项
- 所有时间比对统一用UTC时间,避免两台服务器时区不一致导致的误判
- 遍历文件时加了
-ErrorAction SilentlyContinue参数,遇到权限不足、文件被占用的情况不会直接终止脚本 - 如果需要导出结果,可以把输出的差异对象通过
Export-Csv命令存成表格文件,方便后续排查 - 不建议用更快的非原生哈希算法,MD5是PowerShell原生支持的,不需要额外安装模块,适配所有Windows环境
内容的提问来源于stack exchange,提问作者Keith Jones
相关产品推荐
相关产品推荐

