Powershell 比较对象但忽略特定字符?

Powershell 比较对象但忽略特定字符?

感谢这里的另一位用户,我能够发现 Powershell Compare-Object。

我使用的代码很简单,如下所示:

$array = Compare-Object $(Get-Content $Source1) $(Get-Content $Source2)
$array | where {$_.SideIndicator -eq "<="} | Format-Table -Property InputObject -AutoSize -HideTableHeaders | Out-File -Width 512 -Encoding utf8 NoMatchA.txt
$array | where {$_.SideIndicator -eq "=>"} | Format-Table -Property InputObject -AutoSize -HideTableHeaders | Out-File -Width 512 -Encoding utf8 NoMatchB.txt

我正在比较的文件是哈希值+文件路径,看起来像这个测试输入源A:

0004250736cc617f596b24d69c52a1276ea7f81cd5f7c7e49458894987d02538 D:\Documents\Server\DBackupServer\snapraid\Archive\authors.txt
5a10fd71a62ca272908ded8a7f20826722c0fc67da22256accb42159baa13af2 D:\Documents\Server\DBackupServer\snapraid\Archive\copying.txt
d3d868c751b91c5bd5105db5b9c4f35429fe85a6405bf7fa073d969a0f24a4bc D:\Documents\Server\DBackupServer\snapraid\Archive\history.txt
a1ca63b535996640ac4c5ae0fb91998ebcb495928a637d8d8f71508573c289bd D:\Documents\Server\DBackupServer\snapraid\Archive\install.txt
62f9bfd975bc0773be108b20467d6ece969e06e033b5ae51fb56597d09800000 D:\Documents\Server\DBackupServer\snapraid\Archive\readme.txt

来源B:

0004250736cc617f596b24d69c52a1276ea7f81cd5f7c7e49458894987d00000 E:\Documents\Server\DBackupServer\snapraid\Archive\authors.txt
06d99bb9e6b9c0f57828b29a465a769628da86faca48a104fbdd3263d85eda4d E:\Documents\Server\DBackupServer\snapraid\Archive\backup_snapraid.bat
5a10fd71a62ca272908ded8a7f20826722c0fc67da22256accb42159baa13af2 E:\Documents\Server\DBackupServer\snapraid\Archive\history.txt
a1ca63b535996640ac4c5ae0fb91998ebcb495928a637d8d8f71508573c289bd E:\Documents\Server\DBackupServer\snapraid\Archive\install.txt
62f9bfd975bc0773be108b20467d6ece969e06e033b5ae51fb56597d098969f3 E:\Documents\Server\DBackupServer\snapraid\Archive\readme.txt

但我希望它忽略驱动器号,这样我就可以比较来自两个不同来源的两个文件。现在我通过for /f批处理文件中的命令运行哈希日志文件以删除驱动器号,但对于大量条目来说,这可能需要很长时间。

是否有任何方法可以让比较对象忽略驱动器号?:是驱动器号的唯一标识符(尽管最终希望它与 UNC 前缀一起使用\\),并且考虑到哈希,它应该始终处于相同的位置,所以也许这可能会有所帮助。

我尝试使用该Split()命令,虽然我可以根据空格字符拆分对象,但我不确定如何将其实现到比较对象中。

我尝试使用:get-content $Source1 | foreach {$_ -replace "D:", ""} | Set-Content "$Source1a"这与批处理文件一样长......

谢谢你的帮助。

编辑:感谢大家到目前为止的帮助。我还没有机会测试这个提案,明天应该会测试。

无论如何,为了澄清起见,我正在寻找三个文件输出:

  1. 文件在 A 中而不在 B 中- 输出格式与输入相同(哈希 + 文件路径/名称),但仅比较文件名/路径,以识别源 A 中不在源 B 中的文件。

从上面的测试输入,结果输出将是“FileAnotInB.txt”:

5a10fd71a62ca272908ded8a7f20826722c0fc67da22256accb42159baa13af2 D:\Documents\Server\DBackupServer\snapraid\Archive\copying.txt
  1. 文件在 B 中而不在 A 中- 输出格式与输入相同(哈希 + 文件路径/名称),但仅比较文件名/路径,以识别源 B 中不在源 A 中的文件。

从上面的测试输入,结果输出将是“FileBnotInA.txt”:

06d99bb9e6b9c0f57828b29a465a769628da86faca48a104fbdd3263d85eda4d E:\Documents\Server\DBackupServer\snapraid\Archive\backup_snapraid.bat
  1. 哈希不匹配- 输出格式与输入相同(哈希值 + 文件名路径),但仅比较哈希值,以识别不匹配的哈希值,最好将源 A 和源 B 同时输出以进行简单比较:

从上面的测试输入,结果输出将是(注意在测试哈希末尾替换 00000)“FailedHashes.txt”:

0004250736cc617f596b24d69c52a1276ea7f81cd5f7c7e49458894987d02538 D:\Documents\Server\DBackupServer\snapraid\Archive\authors.txt
0004250736cc617f596b24d69c52a1276ea7f81cd5f7c7e49458894987d00000 D:\Documents\Server\DBackupServer\snapraid\Archive\authors.txt
62f9bfd975bc0773be108b20467d6ece969e06e033b5ae51fb56597d09800000 E:\Documents\Server\DBackupServer\snapraid\Archive\readme.txt
62f9bfd975bc0773be108b20467d6ece969e06e033b5ae51fb56597d098969f3 E:\Documents\Server\DBackupServer\snapraid\Archive\readme.txt

看起来 JosefZ 的回复可能会这样做,或者至少我可以调整它来做到这一点。我必须测试一下才能知道,但我想编辑一下以澄清并感谢您的帮助!

答案1

以下代码片段展示了如何忽略驱动器号的可能方法:

Compare-Object $((Get-Content $Source1) -replace "\s[A-Z]\:\\", ' \') $((Get-Content $Source2) -replace "\s[A-Z]\:\\", ' \')

… 有没有一种简单的方法,也可以使用 compare-object 和/或 where-object 来输出一个文件,显示文件名匹配但哈希文件不匹配的位置?我最终的目标是三个文件:源 A 中的文件不在 B 中。源 B 中的文件不在 A 中。然后是文件名/路径相同但哈希文件不匹配?

我不确定这种三分法是否如上述补充要求中所述定义明确。不过,这是我解决它的尝试(略微更改了变量和文件名):

$sourcePath = 'D:\PShell\DataFiles'
$utf8     = 'utf8'     # $utf8 = 'Default'  # debugged using Default
$sourceA  = "$sourcePath\1558327A.txt"
$sourceB  = "$sourcePath\1558327B.txt"
$contentA = (Get-Content -Path $SourceA)
$contentB = (Get-Content -Path $SourceB)
$array = Compare-Object -ReferenceObject  $(
            $contentA -replace "\s[A-Z]\:\\", ' \')  -DifferenceObject $(
            $contentB -replace "\s[A-Z]\:\\", ' \')
$arrNotInB = $array | Where-Object {$_.SideIndicator -eq "<="} # |    Select-Object -ExpandProperty InputObject #| 
$arrNotInA = $array | Where-Object {$_.SideIndicator -eq "=>"} # |    Select-Object -ExpandProperty InputObject #|

# Files in Source B not in A. 
$arrNotInA |
    Select-Object -ExpandProperty InputObject |
    Where-Object {
        -not ($arrNotInB -match [regex]::Escape($($_ -split '\s', 2)[1]))
    } | Out-File -Width 512 -Encoding $utf8 -FilePath "$sourcePath\NotInA.txt"
# Files in source A not in B.
$arrNotInB |
    Select-Object -ExpandProperty InputObject |
    Where-Object {
        -not ($arrNotInA -match [regex]::Escape($($_ -split '\s', 2)[1]))
    } | Out-File -Width 512 -Encoding $utf8 -FilePath "$sourcePath\NotInB.txt" 
# And then same filenames/paths but with mismatched hash
(
$arrNotInB |
    Select-Object -ExpandProperty InputObject |
    Where-Object {
        ($arrNotInA -match [regex]::Escape($($_ -split '\s', 2)[1]))
    } | ForEach-Object {
        $auxHash, $auxPath = $_ -split '\s', 2
        $contentA | Where-Object {
            ($_ -replace "\s[A-Z]\:\\", ' \') -match [regex]::Escape("$auxHash $auxPath")
        }
    }
),(
$arrNotInA |
    Select-Object -ExpandProperty InputObject |
    Where-Object {
        ($arrNotInB -match [regex]::Escape($($_ -split '\s', 2)[1]))
    } | ForEach-Object {
        $auxHash, $auxPath = $_ -split '\s', 2
        $contentB | Where-Object {
            ($_ -replace "\s[A-Z]\:\\", ' \') -match [regex]::Escape("$auxHash $auxPath")
        }
    }
) | Out-File -Width 512 -Encoding $utf8 -FilePath "$sourcePath\NotMatchHash.txt"

答案2

样本数据:

# --- SourceA --    --- SourceB --
# --------------    --------------
# 123 c:\123.txt    123 c:\123.txt  <-- FullMatch
# 456 c:\456.txt    789 c:\789.txt  <-- NoMatch
# 0ab c:\0ab.txt    0ab d:\0ab.txt  <-- Hash + RelPathMatch

创建示例文件:

@'
123 c:\123.txt
456 c:\456.txt
0ab c:\0ab.txt
'@ | Set-Content A.txt
@'
123 c:\123.txt
789 c:\789.txt
0ab d:\0ab.txt
'@ | Set-Content B.txt

Import-Csv如果您可以控制源文件的创建,则可以将以下内容简化为一个操作:

Function Import-Log ($Path) {
   Get-Content $Path | ForEach{
      $_ -match '(^.+ )[A-Za-z]\:\\(.+$)' | out-null
      [PSCustomObject]@{
         Full    = $matches[0]
         NoDrive = $matches[1] + $matches[2]
      }
   }
}

根据上述函数定义:

$oA = Import-Log A.txt
$oB = Import-Log B.txt

$NoMatches = Compare-Object $oA $oB -Property NoDrive
$AnotB     = $NoMatches | ? SideIndicator -like '<='
$BnotA     = $NoMatches | ? SideIndicator -like '=>'
$HashMatch = Compare-Object $oA $oB -Property Full, NoDrive | ? NoDrive -notIn $NoMatches.NoDrive

结果:

PS C:\> $NoMatches

NoDrive     SideIndicator
-------     -------------
789 789.txt =>
456 456.txt <=


PS C:\> $AnotB

NoDrive     SideIndicator
-------     -------------
456 456.txt <=


PS C:\> $BnotA

NoDrive     SideIndicator
-------     -------------
789 789.txt =>


PS C:\> $HashMatch

full           NoDrive     SideIndicator
----           -------     -------------
0ab d:\0ab.txt 0ab 0ab.txt =>
0ab c:\0ab.txt 0ab 0ab.txt <=

相关内容