提问人:whitchurch11 提问时间:7/19/2023 更新时间:7/19/2023 访问量:38
Compare-Object 子文件夹目录
Compare-Object sub-folder directories
问:
我有一个大文件夹结构,我正在复制它,然后我想运行比较以确保每个文件都已复制到目标文件夹中。
我拥有的源结构看起来像这样。同一文件名可能存在于多个文件夹中。
- C:\Temp\Source\Folder1\abc.txt
- C:\Temp\Source\Folder2\abc.txt
- C:\Temp\Source\Folder2\File1.txt
作为测试,我故意未能将 abc.txt 复制到 Folder1 到目标文件夹:C:\Temp\Destination\Folder1。
我已经在Powershell中运行了Compare-Object
$Folder1List = Get-ChildItem 'C:\Temp\Source' -Recurse
$Folder2List = Get-ChildItem 'C:\Temp\Destination' -Recurse
Compare-Object $Folder1List $Folder2List
但是,这不会带来差异的结果,因为 abc.txt 存在于某处,只是不一定在我期望的文件夹中。
在比较每个文件夹而不仅仅是在 Folder1 和 Folder2 的根目录中添加任何内容吗?或者我需要单独比较每个子目录,这将是一个痛苦,因为可能有数千个。
答:
我曾经遇到过类似的问题。这是我最终得到的方法:
function Assert-FolderMatches {
<#
.SYNOPSIS
Assert folder matches reference
.DESCRIPTION
Recursively compare folders ensuring that all files and folders have been copied correctly.
If the file length doesn't match it will also raise an error.
.PARAMETER Path
Path to compare.
.PARAMETER ReferencePath
Path to compare with (the source folder for how it should look like).
.PARAMETER AllowNewFiles
Allow files to be present in Path which are not present in ReferencePath (like log files).
#>
Param(
[Parameter(Mandatory = $true)]
[string] $Path,
[Parameter(Mandatory = $true)]
[string] $ReferencePath,
[Parameter()]
[switch] $AllowNewFiles
)
$Path = Resolve-Path $Path
$ReferencePath = Resolve-Path $ReferencePath
# Identify all files and folders.
function Get-ItemsForFolder($FolderPath) {
Get-ChildItem -Path $FolderPath -Recurse | ForEach-Object {
$relativePath = $_.FullName.Replace($FolderPath, "")
if ($relativePath.StartsWith('\') -or $relativePath.StartsWith('/')) {
$relativePath = $relativePath.Substring(1)
}
if ($_ -is [System.IO.DirectoryInfo]) {
[pscustomobject] @{
'RelativePath' = $relativePath;
'Length' = $null;
}
} else {
[pscustomobject] @{
'RelativePath' = $relativePath;
'Length' = $_.Length;
}
}
}
}
$files = Get-ItemsForFolder $Path
$matchFiles = Get-ItemsForFolder $ReferencePath
# Compare items from both sides.
$diff = Compare-Object -ReferenceObject $matchFiles `
-DifferenceObject $files `
-Property RelativePath,Length
# Filter out additions from 'Path' side if requested.
if ($AllowNewFiles) {
$diff = $diff | Where-Object { $_.SideIndicator -ne '=>' }
}
return $diff
}
不是最简单的解决方案,也许有人有更优雅的方法来做到这一点。从本质上讲,我使用相对路径进行比较,而不仅仅是文件名。
下面是一个示例输出。因此,您可以看到“b.txt”同时存在于目标文件夹和源文件夹中,但位于不同的位置,并且它作为差异而引发:
$> Assert-FolderMatches -ReferencePath C:\Temp\a -Path C:\Temp\b
RelativePath Length SideIndicator
------------ ------ -------------
f2 =>
f2\b.txt 0 =>
f1 <=
f1\b.txt 0 <=
请注意,我不是在比较实际的文件内容。我尝试使用散列来做到这一点,但对于我的用例来说太慢了。所以我最终只是比较文件大小,这对我来说似乎是一个合理的折衷方案,除非你真的担心文件以某种方式损坏。
评论
若要比较两个目录树的相应子目录和文件路径(仅),需要按它们的相对路径进行比较,这就是 Get-ChildItem
的 -Name
开关的作用:
# -Name ensures that *relative paths* are returned.
$Folder1List = Get-ChildItem -Name 'C:\Temp\Source' -Recurse
$Folder2List = Get-ChildItem -Name 'C:\Temp\Destination' -Recurse
Compare-Object $Folder1List $Folder2List
请注意,只会导致返回路径字符串,而不是通常的 [System.IO.FileInfo]
和 [System.IO.DirectoryInfo]
实例,因此 Compare-Object
的输出对象的属性将仅包含这些路径字符串(可以在这些字符串上使用 Get-Item
来获取上述类型)。-Name
.InputObject
-LiteralPath
至于你尝试过什么:
当
Compare-Object
比较其类型未实现[System.IComparable]
接口的对象时,将按其值进行比较,这对于输出的对象也是如此。.ToString()
Get-ChildItem
在 Windows PowerShell 中,以及实例(例如使用命令)中,仅按文件名进行字符串化,如本答案中所述。
FileInfo
DirectoryInfo
- 因此,即使位于不同子目录中的文件和目录的名称匹配,如果它们的名称匹配,也会进行比较,正如您所经历的那样。
相比之下,在 PowerShell (Core) 7+ 中,它们现在始终按其完整路径(属性)进行字符串化。
.FullName
- 因此,假设所有路径的开头在要比较的目录树之间不同,则代码会将所有路径报告为不同。
评论