比较Powershell中的Filehash
我是一个超级地狱。下午的大部分时间我都在盯着屏幕,试图找出如何比较两个不同目录中多个文件的文件散列。该脚本将文件从FTP站点下载到一个目录($cDlPath)中,并最终将它们复制到另一个目录($cDestPath)。我想比较文件中的filehash,以确保下载后没有任何更改。我正在使用Get-Hash cmdlet获取文件哈希,但我不知道如何比较这两个哈希。如果文件不相等,我希望能够通过名称识别更改的文件,以便可以检查文件 我一直在摆弄下面的代码,但它似乎不是我想要的比较Powershell中的Filehash,powershell,hash,Powershell,Hash,我是一个超级地狱。下午的大部分时间我都在盯着屏幕,试图找出如何比较两个不同目录中多个文件的文件散列。该脚本将文件从FTP站点下载到一个目录($cDlPath)中,并最终将它们复制到另一个目录($cDestPath)。我想比较文件中的filehash,以确保下载后没有任何更改。我正在使用Get-Hash cmdlet获取文件哈希,但我不知道如何比较这两个哈希。如果文件不相等,我希望能够通过名称识别更改的文件,以便可以检查文件 我一直在摆弄下面的代码,但它似乎不是我想要的 Compare-Objec
Compare-Object `
-ReferenceObject $(Get-ChildItem $cDestPath -Recurse | Where-Object {!$_.psiscontainer } | Get-Hash -Algorithm $cHashAlg) `
-DifferenceObject $(Get-ChildItem $cDlPath -Recurse | Where-Object {!$_.psiscontainer } | Get-Hash -Algorithm $cHashAlg)
任何帮助都将不胜感激
我正在使用下面的代码,我似乎更接近
Compare-Object $(Get-ChildItem $cDlPath -Recurse $_ | Where-Object { !$_.PsIsContainer } |
Select-Object Name, FullName, Length, @{Name=”SHA256 Hash”; Expression={ Get-Hash $_.FullName
-Algorithm "SHA256" }}, LastWriteTime) $( Get-ChildItem $cDestPath -Recurse $_ | Where-Object
{ !$_.PsIsContainer } | Select-Object Name, FullName, Length, @{Name=”SHA256 Hash”;
Expression={ Get-Hash $_.FullName -Algorithm "SHA256" }}, LastWriteTime) -property @
("Name", “FullName”,”SHA256 Hash”, "Length", "LastWriteTime" ) | Add-Content -Path $cLogFile
尽管如此,它看起来仍然不完全正确,因为有一些散列是相同的,并且日志文件的输出是丑陋的。只有当文件具有相同的哈希值时,它们才应位于日志文件中
@{Name=nothing.xlsx; FullName=C:\Test\nothing.xlsx; SHA256 Hash=E74424B6324DE014CB0C896DA29D67A2A729E31DF57119E840CA4BD9A9E41754; Length=8891; LastWriteTime=7/31/2012 1:33:11 PM; SideIndicator=<=}
@{Name=test.txt; FullName=C:\Test\test.txt; SHA256 Hash=FC43E73579DB001751A29C1F7A8E2E36E46A53662B63013F0AE500AA896DE056; Length=174; LastWriteTime=7/31/2012 4:52:52 PM; SideIndicator=<=}
@{Name=testfile.txt; FullName=C:\Test\testfile.txt; SHA256 Hash=2B2DB80CAF93224A49A7C94E8EA5BCB1B86D421EA2DB83285149ECAE6DEAA105; Length=415; LastWriteTime=7/27/2012 12:01:21 PM; SideIndicator=<=}
@{Name=nothing.xlsx; FullName=C:\Test\Old\nothing.xlsx; SHA256 Hash=22603417927343A485862CE93790203EE7C2DB092C2060C92D44B736A01FD37E; Length=8978; LastWriteTime=7/31/2012 4:40:43 PM; SideIndicator=<=}
@{Name=test.txt; FullName=C:\Test\Old\test.txt; SHA256 Hash=FC43E73579DB001751A29C1F7A8E2E36E46A53662B63013F0AE500AA896DE056; Length=174; LastWriteTime=7/31/2012 4:52:52 PM; SideIndicator=<=}
@{Name=testfile.txt; FullName=C:\Test\Old\testfile.txt; SHA256 Hash=0B35A9F7F500B46469E2C1759F92D222983C4FDF4AAE316C0F2861FC70D0FD2B; Length=447; LastWriteTime=7/31/2012 4:52:40 PM; SideIndicator=<=}
@{Name=nothing.xlsx;FullName=C:\Test\nothing.xlsx;SHA256 Hash=E74424B6324DE014CB0C896DA29D67A2A729E31DF57119E840CA4BD9A9E41754;Length=8891;LastWriteTime=7/31/2012 1:33:11 PM;SideIndicator=因此,这里有一个未经测试的解决方案,至少可以让您朝着正确的方向前进:
#first make a hash table of the files in folder 1 where the keys are the file hashes and the values are the file objects
$folder1Files = @{}
foreach($file in $cDestPath){
$hash = Get-Hash $file
if($folder1Files.ContainsKey($hash)){
# A hash collision isn't likely but not unheard of. You should probably just handle them manually
'There was a hash collision for {0} and {1} in folder {2}' -f $file.Name, $folder1Files[$hash].Name, $cDestPath
}else{
$folder1Files[$hash] = $file
}
}
# Now do the same thing for folder 2
$folder2Files = @{}
foreach($file in $cDlPath){
$hash = Get-Hash $file
if($folder2Files.ContainsKey($hash)){
# A hash collision isn't likely but not unheard of. You should probably just handle them manually
'There was a hash collision for {0} and {1} in folder {2}' -f $file.Name, $folder1Files[$hash].Name, $cDlPath
}else{
$folder2Files[$hash] = $file
}
}
# Actually you should really take those two bits and generalize them to a function that you pass a folder to and it returns the hash table.
# Now that you have your two sets of file hashes, use Compare-Object to find the diffs
$comparison = Compare-Object $folder1Files.Keys $folder2Files.Keys
foreach($diff in $comparison){
if($diff.SideIndicator -eq '<='){
'File {0} in folder {1} is different from any file in the other folder' -f $folder1Files[$diff.InputObject], $cDestPath
}else{
'File {0} in folder {1} is different from any file in the other folder' -f $folder2Files[$diff.InputObject], $cDLPath
}
}
#首先创建文件夹1中文件的哈希表,其中键是文件哈希,值是文件对象
$folder1Files=@{}
foreach($cDestPath中的文件){
$hash=获取hash$文件
if($folder1Files.ContainsKey($hash)){
#散列冲突不太可能发生,但也并非闻所未闻。您应该手动处理它们
'文件夹{2}'-f$file.Name,$folder1Files[$hash].Name,$cDestPath中的{0}和{1}发生哈希冲突
}否则{
$folder1Files[$hash]=$file
}
}
#现在对文件夹2执行相同的操作
$folder2Files=@{}
foreach($cDlPath中的文件){
$hash=获取hash$文件
if($folder2Files.ContainsKey($hash)){
#散列冲突不太可能发生,但也并非闻所未闻。您应该手动处理它们
'文件夹{2}'-f$file.Name,$folder1Files[$hash].Name,$cDlPath中的{0}和{1}发生哈希冲突
}否则{
$folder2Files[$hash]=$file
}
}
#实际上,您应该将这两个位泛化为一个函数,将文件夹传递给该函数,它将返回哈希表。
#现在有了两组文件哈希,使用Compare对象来查找差异
$comparison=比较对象$folder1Files.Keys$folder2Files.Keys
foreach($比较中的差异){
如果($diff.SideIndicator-eq',这对我来说非常有效-比较源代码的两个分支。计数为1的组在另一个分支中没有MD5哈希双胞胎,例如:
$paths = 'c:\proj\trunk\source','c:\proj\branches\release\1.0\source'
ls $paths -r *.cs | Where {$_.PSPath -notmatch '\\obj\\'} |
Get-Hash | Select Path,HashString | Group HashString |
Where {$_.Count -eq 1} | Sort Count -desc | Format-List
必须有一个更漂亮的解决方案,但这就是我最终使用的
# Get the file hashes
$hashsourcefile = Get-Hash $file -Algorithm "SHA256"
$hashdestfile = Get-Hash $file2 -Algorithm "SHA256"
# Compare the hashes
Compare-Object -Referenceobject $hashsourcefile -Differenceobject $hashdestfile | % { If ($_.Sideindicator -ne " ==") {$diff+=1} }
# The Hashes are different. Note this in the log
if ($diff -ne 0)
{
Add-Content -Path $cLogFile -Value " Source File Hash: $hashsourcefile does not equal
Existing Destination File Hash: $hashdestfile the files are NOT EQUAL."
}
我意识到这是一个陈旧的线程(16个月前),但这是谷歌的最高结果,这意味着它得到了很多的意见。我认为这可能会有利于其他人
Mack是对的,使用Get Hash有一个更漂亮、更简单的解决方案。您可以通过比较哈希来简单地比较IF语句中的哈希
# Get the file hashes
$hashSrc = Get-FileHash $file -Algorithm "SHA256"
$hashDest = Get-FileHash $file2 -Algorithm "SHA256"
# Compare the hashes & note this in the log
If ($hashSrc.Hash -ne $hashDest.Hash)
{
Add-Content -Path $cLogFile -Value " Source File Hash: $hashSrc does not
equal Existing Destination File Hash: $hashDest the files are NOT EQUAL."
}
使用MD5哈希的递归目录文件内容差异
我编写了这个纯PowerShell v3+(无依赖项)递归目录diff,它通过MD5散列比较内容
参数,它将导出左侧和右侧目录的一组csv差异以及差异的摘要文件。否则,将在标准输出上给出标准的比较对象差异结果。您可以将rdiff.ps1文件放到路径中并设置ExecutionPolicy RemoteSigned
,或者将内容直接复制到脚本中
用法:rdiff path/to/left,path/to/right[-s path/to/summary/dir]
以下是建议使用gist版本的。因为它可能在下面的版本上添加了其他功能
#########################################################################
### USAGE: rdiff path/to/left,path/to/right [-s path/to/summary/dir] ###
### ADD LOCATION OF THIS SCRIPT TO PATH ###
#########################################################################
[CmdletBinding()]
param (
[parameter(HelpMessage="Stores the execution working directory.")]
[string]$ExecutionDirectory=$PWD,
[parameter(Position=0,HelpMessage="Compare two directories recursively for differences.")]
[alias("c")]
[string[]]$Compare,
[parameter(HelpMessage="Export a summary to path.")]
[alias("s")]
[string]$ExportSummary
)
### FUNCTION DEFINITIONS ###
# SETS WORKING DIRECTORY FOR .NET #
function SetWorkDir($PathName, $TestPath) {
$AbsPath = NormalizePath $PathName $TestPath
Set-Location $AbsPath
[System.IO.Directory]::SetCurrentDirectory($AbsPath)
}
# RESTORES THE EXECUTION WORKING DIRECTORY AND EXITS #
function SafeExit() {
SetWorkDir /path/to/execution/directory $ExecutionDirectory
Exit
}
function Print {
[CmdletBinding()]
param (
[parameter(Mandatory=$TRUE,Position=0,HelpMessage="Message to print.")]
[string]$Message,
[parameter(HelpMessage="Specifies a success.")]
[alias("s")]
[switch]$SuccessFlag,
[parameter(HelpMessage="Specifies a warning.")]
[alias("w")]
[switch]$WarningFlag,
[parameter(HelpMessage="Specifies an error.")]
[alias("e")]
[switch]$ErrorFlag,
[parameter(HelpMessage="Specifies a fatal error.")]
[alias("f")]
[switch]$FatalFlag,
[parameter(HelpMessage="Specifies a info message.")]
[alias("i")]
[switch]$InfoFlag = !$SuccessFlag -and !$WarningFlag -and !$ErrorFlag -and !$FatalFlag,
[parameter(HelpMessage="Specifies blank lines to print before.")]
[alias("b")]
[int]$LinesBefore=0,
[parameter(HelpMessage="Specifies blank lines to print after.")]
[alias("a")]
[int]$LinesAfter=0,
[parameter(HelpMessage="Specifies if program should exit.")]
[alias("x")]
[switch]$ExitAfter
)
PROCESS {
if($LinesBefore -ne 0) {
foreach($i in 0..$LinesBefore) { Write-Host "" }
}
if($InfoFlag) { Write-Host "$Message" }
if($SuccessFlag) { Write-Host "$Message" -ForegroundColor "Green" }
if($WarningFlag) { Write-Host "$Message" -ForegroundColor "Orange" }
if($ErrorFlag) { Write-Host "$Message" -ForegroundColor "Red" }
if($FatalFlag) { Write-Host "$Message" -ForegroundColor "Red" -BackgroundColor "Black" }
if($LinesAfter -ne 0) {
foreach($i in 0..$LinesAfter) { Write-Host "" }
}
if($ExitAfter) { SafeExit }
}
}
# VALIDATES STRING MIGHT BE A PATH #
function ValidatePath($PathName, $TestPath) {
If([string]::IsNullOrWhiteSpace($TestPath)) {
Print -x -f "$PathName is not a path"
}
}
# NORMALIZES RELATIVE OR ABSOLUTE PATH TO ABSOLUTE PATH #
function NormalizePath($PathName, $TestPath) {
ValidatePath "$PathName" "$TestPath"
$TestPath = [System.IO.Path]::Combine((pwd).Path, $TestPath)
$NormalizedPath = [System.IO.Path]::GetFullPath($TestPath)
return $NormalizedPath
}
# VALIDATES STRING MIGHT BE A PATH AND RETURNS ABSOLUTE PATH #
function ResolvePath($PathName, $TestPath) {
ValidatePath "$PathName" "$TestPath"
$ResolvedPath = NormalizePath $PathName $TestPath
return $ResolvedPath
}
# VALIDATES STRING RESOLVES TO A PATH AND RETURNS ABSOLUTE PATH #
function RequirePath($PathName, $TestPath, $PathType) {
ValidatePath $PathName $TestPath
If(!(Test-Path $TestPath -PathType $PathType)) {
Print -x -f "$PathName ($TestPath) does not exist as a $PathType"
}
$ResolvedPath = Resolve-Path $TestPath
return $ResolvedPath
}
# Like mkdir -p -> creates a directory recursively if it doesn't exist #
function MakeDirP {
[CmdletBinding()]
param (
[parameter(Mandatory=$TRUE,Position=0,HelpMessage="Path create.")]
[string]$Path
)
PROCESS {
New-Item -path $Path -itemtype Directory -force | Out-Null
}
}
# GETS ALL FILES IN A PATH RECURSIVELY #
function GetFiles {
[CmdletBinding()]
param (
[parameter(Mandatory=$TRUE,Position=0,HelpMessage="Path to get files for.")]
[string]$Path
)
PROCESS {
ls $Path -r | where { !$_.PSIsContainer }
}
}
# GETS ALL FILES WITH CALCULATED HASH PROPERTY RELATIVE TO A ROOT DIRECTORY RECURSIVELY #
# RETURNS LIST OF @{RelativePath, Hash, FullName}
function GetFilesWithHash {
[CmdletBinding()]
param (
[parameter(Mandatory=$TRUE,Position=0,HelpMessage="Path to get directories for.")]
[string]$Path,
[parameter(HelpMessage="The hash algorithm to use.")]
[string]$Algorithm="MD5"
)
PROCESS {
$OriginalPath = $PWD
SetWorkDir path/to/diff $Path
GetFiles $Path | select @{N="RelativePath";E={$_.FullName | Resolve-Path -Relative}},
@{N="Hash";E={(Get-FileHash $_.FullName -Algorithm $Algorithm | select Hash).Hash}},
FullName
SetWorkDir path/to/original $OriginalPath
}
}
# COMPARE TWO DIRECTORIES RECURSIVELY #
# RETURNS LIST OF @{RelativePath, Hash, FullName}
function DiffDirectories {
[CmdletBinding()]
param (
[parameter(Mandatory=$TRUE,Position=0,HelpMessage="Directory to compare left.")]
[alias("l")]
[string]$LeftPath,
[parameter(Mandatory=$TRUE,Position=1,HelpMessage="Directory to compare right.")]
[alias("r")]
[string]$RightPath
)
PROCESS {
$LeftHash = GetFilesWithHash $LeftPath
$RightHash = GetFilesWithHash $RightPath
diff -ReferenceObject $LeftHash -DifferenceObject $RightHash -Property RelativePath,Hash
}
}
### END FUNCTION DEFINITIONS ###
### PROGRAM LOGIC ###
if($Compare.length -ne 2) {
Print -x "Compare requires passing exactly 2 path parameters separated by comma, you passed $($Compare.length)." -f
}
Print "Comparing $($Compare[0]) to $($Compare[1])..." -a 1
$LeftPath = RequirePath path/to/left $Compare[0] container
$RightPath = RequirePath path/to/right $Compare[1] container
$Diff = DiffDirectories $LeftPath $RightPath
$LeftDiff = $Diff | where {$_.SideIndicator -eq "<="} | select RelativePath,Hash
$RightDiff = $Diff | where {$_.SideIndicator -eq "=>"} | select RelativePath,Hash
if($ExportSummary) {
$ExportSummary = ResolvePath path/to/summary/dir $ExportSummary
MakeDirP $ExportSummary
$SummaryPath = Join-Path $ExportSummary summary.txt
$LeftCsvPath = Join-Path $ExportSummary left.csv
$RightCsvPath = Join-Path $ExportSummary right.csv
$LeftMeasure = $LeftDiff | measure
$RightMeasure = $RightDiff | measure
"== DIFF SUMMARY ==" > $SummaryPath
"" >> $SummaryPath
"-- DIRECTORIES --" >> $SummaryPath
"`tLEFT -> $LeftPath" >> $SummaryPath
"`tRIGHT -> $RightPath" >> $SummaryPath
"" >> $SummaryPath
"-- DIFF COUNT --" >> $SummaryPath
"`tLEFT -> $($LeftMeasure.Count)" >> $SummaryPath
"`tRIGHT -> $($RightMeasure.Count)" >> $SummaryPath
"" >> $SummaryPath
$Diff | Format-Table >> $SummaryPath
$LeftDiff | Export-Csv $LeftCsvPath -f
$RightDiff | Export-Csv $RightCsvPath -f
}
$Diff
SafeExit
#########################################################################
###用法:rdiff path/to/left,path/to/right[-s path/to/summary/dir]###
###将此脚本的位置添加到路径###
#########################################################################
[CmdletBinding()]
param(
[参数(HelpMessage=“存储执行工作目录”。)]
[string]$ExecutionDirectory=$PWD,
[参数(Position=0,HelpMessage=“递归比较两个目录的差异。”)]
[别名(“c”)]
[string[]]$Compare,
[参数(HelpMessage=“将摘要导出到路径。”)]
[别名(“s”)]
[字符串]$ExportSummary
)
###函数定义###
#设置.NET的工作目录#
函数SetWorkDir($PathName,$TestPath){
$AbsPath=NormalizePath$PathName$TestPath
设置位置$AbsPath
[System.IO.Directory]::SetCurrentDirectory($AbsPath)
}
#恢复执行工作目录并退出#
函数SafeExit(){
SetWorkDir/path/to/execution/directory$ExecutionDirectory
出口
}
函数打印{
[CmdletBinding()]
param(
[参数(必需=$TRUE,位置=0,HelpMessage=“要打印的消息”)]
[字符串]$Message,
[参数(HelpMessage=“指定成功。”)]
[别名(“s”)]
[开关]$SuccessFlag,
[参数(HelpMessage=“指定警告。”)]
[别名(“w”)]
[开关]$WarningFlag,
[参数(HelpMessage=“指定错误。”)]
[别名(“e”)]
[开关]$ErrorFlag,
[参数(HelpMessage=“指定致命错误。”)]
[别名(“f”)]
[开关]$FatalFlag,
[参数(HelpMessage=“指定信息消息。”)]
[别名(“i”)]
[switch]$InfoFlag=!$SuccessFlag-和!$WarningFlag-和!$ErrorFlag-和!$FatalFlag,
[参数(HelpMessage=“指定要打印的空行。”)]
[别名(“b”)]
[int]$LinesBefore=0,
[参数(HelpMessage=“指定打印后的空行。”