【问题标题】:Powershell script to compare two directories (including sub directories and contents) that are supposed to be identical but on different servers用于比较两个应该相同但位于不同服务器上的目录(包括子目录和内容)的 Powershell 脚本
【发布时间】:2022-11-12 20:02:45
【问题描述】:

我想运行一个 powershell 脚本,该脚本可以由用户提供目录名称,然后它将检查目录、子目录和这些目录的所有文件内容,以比较它们是否彼此相同。有 8 台服务器应该都有相同的文件和内容。下面的代码似乎没有按照我的意图做。我已经看到了 Compare-Object、Get-ChildItem 和 Get-FileHash 的使用,但还没有找到我确信确实可以完成任务的正确组合。任何和所有的帮助表示赞赏!

$35 = "\\server1\"
$36 = "\\server2\"
$37 = "\\server3\"
$38 = "\\server4\"
$45 = "\\server5\"
$46 = "\\server6\"
$47 = "\\server7\"
$48 = "\\server8\"
do{
Write-Host "|1 : New   |"
Write-Host "|2 : Repeat|"
Write-Host "|3 : Exit  |"
$choice = Read-Host -Prompt "Please make a selection"
    switch ($choice){
        1{
            $App = Read-Host -Prompt "Input Directory Application"
        }
        2{
            #rerun
        }
    3{
        exit;       }
    }

$c35 = $35 + "$App" +"\*"
$c36 = $36 + "$App" +"\*"
$c37 = $37 + "$App" +"\*"
$c38 = $38 + "$App" +"\*"
$c45 = $45 + "$App" +"\*"
$c46 = $46 + "$App" +"\*"
$c47 = $47 + "$App" +"\*"
$c48 = $48 + "$App" +"\*"

Write-Host "Comparing Server1 -> Server2"
if((Get-ChildItem $c35 -Recurse | Get-FileHash | Select-Object Hash,Path).hash -eq (Get-ChildItem $c36 -Recurse | Get-FileHash | Select-Object Hash,Path).hash){"Identical"}else{"NOT Identical"}

Write-Host "Comparing Server1 -> Server3"
if((Get-ChildItem $c35 -Recurse | Get-FileHash | Select-Object Hash,Path).hash -eq (Get-ChildItem $c37 -Recurse | Get-FileHash | Select-Object Hash,Path).hash){"Identical"}else{"NOT Identical"}

Write-Host "Comparing Server1 -> Server4"
if((Get-ChildItem $c35 -Recurse | Get-FileHash | Select-Object Hash,Path).hash -eq (Get-ChildItem $c38 -Recurse | Get-FileHash | Select-Object Hash,Path).hash){"Identical"}else{"NOT Identical"}

Write-Host "Comparing Server1 -> Server5"
if((Get-ChildItem $c35 -Recurse | Get-FileHash | Select-Object Hash,Path).hash -eq (Get-ChildItem $c45 -Recurse | Get-FileHash | Select-Object Hash,Path).hash){"Identical"}else{"NOT Identical"}

Write-Host "Comparing Server1 -> Server6"
if((Get-ChildItem $c35 -Recurse | Get-FileHash | Select-Object Hash,Path).hash -eq (Get-ChildItem $c46 -Recurse | Get-FileHash | Select-Object Hash,Path).hash){"Identical"}else{"NOT Identical"}

Write-Host "Comparing Server1 -> Server7"
if((Get-ChildItem $c35 -Recurse | Get-FileHash | Select-Object Hash,Path).hash -eq (Get-ChildItem $c47 -Recurse | Get-FileHash | Select-Object Hash,Path).hash){"Identical"}else{"NOT Identical"}

Write-Host "Comparing Server1 -> Server8"
if((Get-ChildItem $c35 -Recurse | Get-FileHash | Select-Object Hash,Path).hash -eq (Get-ChildItem $c48 -Recurse | Get-FileHash | Select-Object Hash,Path).hash){"Identical"}else{"NOT Identical"}

} until ($choice -eq 3)

【问题讨论】:

  • -eq 不会按照您的意愿逐一比较数组。相反,它通过 RHS 操作数过滤 LHS 数组操作数。使用Compare-Object 比较数组。顺便说一句,在比较之前计算所有文件哈希会非常非常慢。一种更快的方法是仅计算第一个目录的所有文件哈希。对于后续目录,计算一个哈希值,然后立即与第一个目录中具有相同相对路径的文件进行比较。如果不同,则不需要计算目录的剩余哈希值。
  • 我打赌 robocopy 可以更快地做到这一点。
  • 您应该将所有服务器放在一个数组中,没有理由将它们放在单独的变量中
  • 您想每次在每台服务器上比较路径输入的文件夹/内容吗?在任何时间段内其他文件夹是否有任何更改?
  • @zett42,您能否提供一些示例代码来说明您的意思?

标签: powershell server directory


【解决方案1】:

这是一个示例函数,它试图比较一个参考针对多个目录区别目录有效。它通过首先比较最容易获得的信息和停在第一个差异.

  • 获取有关文件的所有相关信息参考目录一次,包括散列(尽管这可以通过仅在必要时获取散列来更优化)。
  • 对于每个区别目录,按以下顺序比较:
    • 文件数- 如果不同,那么显然目录是不同的
    • 相对文件路径- 如果不是所有路径区别目录可以在参考目录,然后目录不同
    • 文件大小- 应该是显而易见的
    • 文件哈希- 仅当文件大小相等时才需要计算哈希值
Function Compare-MultipleDirectories {
    param(
        [Parameter(Mandatory)] [string] $ReferencePath,
        [Parameter(Mandatory)] [string[]] $DifferencePath
    )

    # Get basic file information recursively by calling Get-ChildItem with the addition of the relative file path
    Function Get-ChildItemRelative {
        param( [Parameter(Mandatory)] [string] $Path )

        Push-Location $Path  # Base path for Get-ChildItem and Resolve-Path
        try { 
            Get-ChildItem -File -Recurse | 
                Select-Object FullName, Length, @{ n = 'RelativePath'; e = { Resolve-Path $_.FullName -Relative } }
        } finally { 
            Pop-Location 
        }
    }

    Write-Verbose "Reading reference directory '$ReferencePath'"

    # Create hashtable with all infos of reference directory
    $refFiles = @{}
    Get-ChildItemRelative $ReferencePath |
        Select-Object *, @{ n = 'Hash'; e = { (Get-FileHash $_.FullName -Algorithm MD5).Hash } } | 
        ForEach-Object { $refFiles[ $_.RelativePath ] = $_ }

    # Compare content of each directory of $DifferencePath with $ReferencePath
    foreach( $diffPath in $DifferencePath ) {
        Write-Verbose "Comparing directory '$diffPath' with '$ReferencePath'"
        
        $areDirectoriesEqual = $false
        $differenceType = $null

        $diffFiles = Get-ChildItemRelative $diffPath

        # Directories must have same number of files
        if( $diffFiles.Count -eq $refFiles.Count ) {

            # Find first different path (if any)
            $firstDifferentPath = $diffFiles | Where-Object { -not $refFiles.ContainsKey( $_.RelativePath ) } | 
                                               Select-Object -First 1

            if( -not $firstDifferentPath ) {

                # Find first different content (if any) by file size comparison
                $firstDifferentFileSize = $diffFiles |
                    Where-Object { $refFiles[ $_.RelativePath ].Length -ne $_.Length } |
                    Select-Object -First 1

                if( -not $firstDifferentFileSize ) {

                    # Find first different content (if any) by hash comparison
                    $firstDifferentContent = $diffFiles | 
                        Where-Object { $refFiles[ $_.RelativePath ].Hash -ne (Get-FileHash $_.FullName -Algorithm MD5).Hash } | 
                        Select-Object -First 1
                
                    if( -not $firstDifferentContent ) {
                        $areDirectoriesEqual = $true
                    }
                    else {
                        $differenceType = 'Content'
                    } 
                }
                else {
                    $differenceType = 'FileSize'
                }
            }
            else {
                $differenceType = 'Path'
            }
        }
        else {
            $differenceType = 'FileCount'
        }

        # Output comparison result
        [PSCustomObject]@{ 
            ReferencePath = $ReferencePath  
            DifferencePath = $diffPath  
            Equal = $areDirectoriesEqual  
            DiffCause = $differenceType 
        }
    }
}

使用示例:

# compare each of directories B, C, D, E, F against A
Compare-MultipleDirectories -ReferencePath 'A' -DifferencePath 'B', 'C', 'D', 'E', 'F' -Verbose

输出示例:

ReferencePath DifferencePath Equal DiffCause
------------- -------------- ----- ---------
A             B               True 
A             C              False FileCount
A             D              False Path     
A             E              False FileSize 
A             F              False Content 

DiffCause 列为您提供函数认为目录不同的原因的信息。

笔记:

  • Select-Object -First 1 是在我们得到第一个结果后停止搜索的巧妙技巧。它是高效的,因为它不会首先处理所有输入并删除除第一项之外的所有内容,而是在找到第一项后实际上取消管道。
  • Group-Object RelativePath -AsHashTable 创建文件信息的hashtable,以便可以通过RelativePath 属性快速查找。
  • 空子目录被忽略,因为该函数只查看文件。例如。如果引用路径包含一些空目录但差异路径不包含,并且所有其他目录中的文件相等,则该函数将目录视为相等。
  • 我选择了 MD5 算法,因为它比 Get-FileHash 使用的默认 SHA-256 算法更快,但它是不安全.有人可以轻松地操纵不同的文件,使其具有与原始文件相同的 MD5 哈希值。不过,在受信任的环境中,这无关紧要。如果您需要更安全的比较,请删除 -Algorithm MD5

【讨论】:

  • 这是一个惊人的反应!明天我会调查第一件事,让你知道它是怎么回事。太感谢了!
  • 嘿 @zett42,我试过运行它,但它似乎无法正常工作。我在两个不同的位置创建了两个相同的文件夹,看起来该功能在 PATH 上失败了。这些是我在测试时指向的两个文件夹。 “\networklocationuserdir$TForeTest CTest A”和“\networklocationuserdir$TForeTest A”。文件夹 Test A 是第一个文件夹的直接副本。如果我做错了什么,请告诉我。谢谢!
  • @TFore说实话,我没有用UNC路径测试过这个函数,甚至没有用绝对路径测试过。所以实际上可能有一个错误。让我在几个小时后仔细看看,当我有时间的时候。
  • @TFore 我可以用 PowerShell 5.1 重现该问题。它仅适用于 PowerShell 7.3。我还没有弄清楚 PS 5.1 问题的原因(我有点累,明天再仔细看看)。
  • @TFore 我已经找到问题并修复了 PS 5.1 的代码!与 PS 7.3 相比,Group-Object -AsHashtable 在 PS 5.1 中的工作方式显然不同。代码在hashtable 中找不到任何键,因此它认为路径不同。我仍然不知道Group-Object 在 PS 5.1 中有什么问题,所以我只是删除它并直接创建哈希表。这现在适用于 PS 5.1 和 7.3。我还使用完整路径测试了它,但没有使用 UNC 路径,但我预计 UNC 路径不会有任何困难。
猜你喜欢
  • 2019-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-24
  • 1970-01-01
  • 1970-01-01
  • 2013-07-08
  • 1970-01-01
相关资源
最近更新 更多