11

私はPowerShellです。私は午後のほとんどの間、2つの異なるディレクトリにある複数のファイルのファイルハッシュを比較する方法を見つけようとして、画面を見つめてきました。スクリプトは、FTPサイトからディレクトリ($ cDlPath)にファイルをダウンロードし、最終的に別のディレクトリ($ cDestPath)にコピーします。ファイルのファイルハッシュを比較して、ダウンロードしてから何も変更されていないことを確認したいと思います。Get-Hashコマンドレットを使用してファイルハッシュを取得していますが、2つのハッシュを比較する方法がわかりません。ファイルが等しくない場合は、変更されたファイルを名前で識別できるようにして、ファイルをチェックできるようにします。

私は以下のコードをいじっていますが、それは私が求めているものではないようです。

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=<=}
4

5 に答える 5

1

MD5 ハッシュを使用した再帰的なディレクトリ ファイル コンテンツの差分

MD5 ハッシュを介してコンテンツを比較する、この純粋な PowerShell v3+ (依存関係なし) の再帰的なディレクトリ diff を作成しました。引数を-ExportSummary [summary/path]指定すると、左側と右側のディレクトリの csv 差分のセットが、差分の概要ファイルと共にエクスポートされます。それ以外の場合は、stdout に標準的な Compare-Object diff の結果が表示されます。rdiff.ps1 ファイルをパスにドロップするかSet-ExecutionPolicy RemoteSigned、内容をスクリプトに直接コピーできます。

USAGE: 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
于 2015-09-24T19:42:05.453 に答える
1

もっときれいな解決策が必要ですが、これが私が最終的に使用したものです。

# 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."
}
于 2012-08-20T17:59:54.633 に答える
0

したがって、少なくとも正しい方向に進むはずのテストされていないソリューションを次に示します。

#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
    }
}

私が言うように、今はテストしてバグを解決する時間はありませんが、少なくともこれは、私がこれに取り組む方法の疑似コードとして役立つはずです.

于 2012-07-31T21:09:02.497 に答える
0

ソースコードの2つのブランチを比較すると、これは私にとってはかなりうまくいきます。カウントが 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
于 2012-07-31T22:21:57.393 に答える