Comparing two folder structures with PowerShell

huangapple go评论91阅读模式
英文:

Comparing two folder structures with PowerShell

问题

  1. # 用 PowerShell 比较两个文件夹结构。最后,应该能看到所有只存在于文件夹 A 或文件夹 B 中的文件/文件夹的完整路径的概述。
  2. # 我还想实现一个开关,可以切换以排除文件扩展名。
  3. # 比较应该仅在路径级别进行。所以,当一个文件具有相同的名称但不同的大小时,应将其视为“相等”。
  4. # 到目前为止,代码如下:
  5. # 提示输入要比较的两个文件夹的路径
  6. $folder1 = Read-Host "输入文件夹 A 的路径"
  7. $folder2 = Read-Host "输入文件夹 B 的路径"
  8. # 提示是否忽略文件扩展名
  9. $ignoreExtensions = Read-Host "忽略文件扩展名? (y/n)"
  10. $ignoreExtensions = $ignoreExtensions.ToLower() -eq "y"
  11. # 获取每个文件夹中的文件并将其名称和路径存储在数组中
  12. $dir1Dirs = Get-ChildItem -Recurse -Name $folder1 | ForEach-Object {
  13. $name = $_.Name
  14. if ($ignoreExtensions) {
  15. $name = $name -replace '\.[^.]*$'
  16. }
  17. [PSCustomObject]@{
  18. Name = $name
  19. FullName = $_.FullName
  20. }
  21. }
  22. $dir2Dirs = Get-ChildItem -Recurse -Name $folder2 | ForEach-Object {
  23. $name = $_.Name
  24. if ($ignoreExtensions) {
  25. $name = $name -replace '\.[^.]*$'
  26. }
  27. [PSCustomObject]@{
  28. Name = $name
  29. FullName = $_.FullName
  30. }
  31. }
  32. # 比较两个文件名数组并显示不同的文件的路径
  33. $diff = Compare-Object -ReferenceObject $dir1Dirs -DifferenceObject $dir2Dirs | Where-Object { $_.SideIndicator -eq "=>" }
  34. if ($diff) {
  35. Write-Host "不同的文件:"
  36. $diff | Select-Object -ExpandProperty FullName
  37. } else {
  38. Write-Host "未找到差异。"
  39. }

但我遇到了错误:

  1. PS C:\Windows\system32> D:\compare4.ps1
  2. 输入文件夹 A 的路径: D:\folder1
  3. 输入文件夹 B 的路径: D:\folder2
  4. 忽略文件扩展名? (y/n): y
  5. 不同的文件:
  6. Select-Object : 无法找到属性“FullName”。
  7. 位于 D:\compare4.ps1:36 字符:13
  8. + $diff | Select-Object -ExpandProperty FullName
  9. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  10. + CategoryInfo : InvalidArgument: (@{InputObject=; SideIndicator==>}:PSObject) [Select-Object], PSArgumen
  11. tException
  12. + FullyQualifiedErrorId : ExpandPropertyNotFound,Microsoft.PowerShell.Commands.SelectObjectCommand
  13. Select-Object : 无法找到属性“FullName”。
  14. 位于 D:\compare4.ps1:36 字符:13
  15. + $diff | Select-Object -ExpandProperty FullName
  16. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  17. + CategoryInfo : InvalidArgument: (@{InputObject=; SideIndicator==>}:PSObject) [Select-Object], PSArgumen
  18. tException
  19. + FullyQualifiedErrorId : ExpandPropertyNotFound,Microsoft.PowerShell.Commands.SelectObjectCommand

当将上面的比较行更改为以下行时,错误消失,但没有生成任何输出:

  1. $diff = Compare-Object $dir1Dirs $dir2Dirs -Property Name -IncludeEqual -PassThru | Where-Object { $_.SideIndicator -eq "=>" }

我在这里漏掉了什么?

先谢谢。

  1. <details>
  2. <summary>英文:</summary>
  3. I am trying to compare two folder structures with PowerShell. At the end, I should see an overview of all files/folders with the full path, which are just in folder A or folder B.
  4. I also want to implement a switch which can be toggled to exclude the file extensions.
  5. The comparison should just be done at the path level. So when a file has the same name but a different size, it should be seen as &quot;equal&quot;.
  6. So far, it looks like this:
  7. ```lang-ps1
  8. # Prompt for the paths of the two folders to compare
  9. $folder1 = Read-Host &quot;Enter the path for Folder A&quot;
  10. $folder2 = Read-Host &quot;Enter the path for Folder B&quot;
  11. # Prompt for whether to ignore file extensions
  12. $ignoreExtensions = Read-Host &quot;Ignore file extensions? (y/n)&quot;
  13. $ignoreExtensions = $ignoreExtensions.ToLower() -eq &quot;y&quot;
  14. # Get the files in each folder and store their names and paths in arrays
  15. $dir1Dirs = Get-ChildItem -Recurse -Name $folder1 | ForEach-Object {
  16. $name = $_.Name
  17. if ($ignoreExtensions) {
  18. $name = $name -replace &#39;\.[^.]*$&#39;
  19. }
  20. [PSCustomObject]@{
  21. Name = $name
  22. FullName = $_.FullName
  23. }
  24. }
  25. $dir2Dirs = Get-ChildItem -Recurse -Name $folder2 | ForEach-Object {
  26. $name = $_.Name
  27. if ($ignoreExtensions) {
  28. $name = $name -replace &#39;\.[^.]*$&#39;
  29. }
  30. [PSCustomObject]@{
  31. Name = $name
  32. FullName = $_.FullName
  33. }
  34. }
  35. # Compare the two arrays of file names and display the paths to files that are different
  36. $diff = Compare-Object -ReferenceObject $dir1Dirs -DifferenceObject $dir2Dirs | Where-Object { $_.SideIndicator -eq &quot;=&gt;&quot; }
  37. if ($diff) {
  38. Write-Host &quot;Files that are different:&quot;
  39. $diff | Select-Object -ExpandProperty FullName
  40. } else {
  41. Write-Host &quot;No differences found.&quot;
  42. }

but I get an error:

  1. PS C:\Windows\system32&gt; D:\compare4.ps1
  2. Enter the path for Folder A: D:\folder1
  3. Enter the path for Folder B: D:\folder2
  4. Ignore file extensions? (y/n): y
  5. Files that are different:
  6. Select-Object : Property &quot;FullName&quot; cannot be found.
  7. At D:\compare4.ps1:36 char:13
  8. + $diff | Select-Object -ExpandProperty FullName
  9. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  10. + CategoryInfo : InvalidArgument: (@{InputObject=; SideIndicator==&gt;}:PSObject) [Select-Object], PSArgumen
  11. tException
  12. + FullyQualifiedErrorId : ExpandPropertyNotFound,Microsoft.PowerShell.Commands.SelectObjectCommand
  13. Select-Object : Property &quot;FullName&quot; cannot be found.
  14. At D:\compare4.ps1:36 char:13
  15. + $diff | Select-Object -ExpandProperty FullName
  16. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  17. + CategoryInfo : InvalidArgument: (@{InputObject=; SideIndicator==&gt;}:PSObject) [Select-Object], PSArgumen
  18. tException
  19. + FullyQualifiedErrorId : ExpandPropertyNotFound,Microsoft.PowerShell.Commands.SelectObjectCommand

When change the compare line from above with this one, the error is gone, but no output generated:

  1. $diff = Compare-Object $dir1Dirs $dir2Dirs -Property Name -IncludeEqual -PassThru | Where-Object { $_.SideIndicator -eq &quot;=&gt;&quot; }

What am I missing here?

Thanks in advance.

答案1

得分: 2

你正在尝试通过它们内部文件的_相对路径_来比较文件夹树。

虽然 Get-ChildItem-Name 参数会输出相对路径,但它是_唯一_输出的内容,即它输出的是_字符串_而不是通常的 System.IO.FileInfo(以及 System.IO.DirectoryInfo)实例,这意味着输出不会包含 .FullName 等属性。

如果不考虑可选忽略扩展名的要求,您将能够通过推断要添加的根路径来重建原始完整路径,具体取决于从 Compare-Object 输出中的 .SideIndicator 的值。

为了满足您的要求,您需要:

  • 向您构造的 [pscustomobject] 实例添加一个 .RelativePath 属性,并通过从每个文件的 .FullName 属性中删除_根_路径的子字符串来手动确定相对路径。

  • 使用 -Property RelativePath 按该属性比较自定义对象的数组,还要确保将自定义对象作为整体传递,必须添加 -PassThru

将它们全部整合在一起:

  1. $folder1 = Read-Host "Enter the path for Folder A"
  2. $folder2 = Read-Host "Enter the path for Folder B"
  3. # 提示是否忽略文件扩展名
  4. $ignoreExtensions = 'y' -eq (Read-Host 'Ignore file extensions? (y/n)')
  5. # 获取每个文件夹中的文件,并将它们的相对路径和完整路径存储在数组中,可选地不包括扩展名。
  6. $dir1Dirs, $dir2Dirs = $folder1, $folder2 |
  7. ForEach-Object {
  8. $fullRootPath = Convert-Path -LiteralPath $_
  9. # 构建当前文件夹树的自定义对象数组,并将其作为单个对象*输出*,使用数组构造运算符的一元形式,","
  10. , @(
  11. Get-ChildItem -File -Recurse -LiteralPath $fullRootPath |
  12. ForEach-Object {
  13. $relativePath = $_.FullName.Substring($fullRootPath.Length + 1)
  14. if ($ignoreExtensions) { $relativePath = $relativePath -replace '\.[^.]*$' }
  15. [PSCustomObject] @{
  16. RelativePath = $relativePath
  17. FullName = $_.FullName
  18. }
  19. }
  20. )
  21. }
  22. # 比较这两个数组。
  23. # 请注意使用了 -Property RelativePath 和 -PassThru,
  24. # 以及 Where-Object SideIndicator -eq '=>' 过滤器,就像您的问题中一样,只报告与 -DifferenceObject 集合不同的内容。
  25. # 要报告与*任一*集合不同的内容,只需删除该过滤器。
  26. $diff =
  27. Compare-Object -Property RelativePath -PassThru $dir1Dirs $dir2Dirs |
  28. Where-Object SideIndicator -eq '=>'
  29. # 输出结果。
  30. if ($diff) {
  31. Write-Host "Files that are different:"
  32. $diff | Select-Object -ExpandProperty FullName
  33. } else {
  34. Write-Host "No differences found."
  35. }

注意:

  • 使用 , @(...) 确保 Get-ChildItem 输出对象作为数组_整体输出_(单个对象)的方式在此答案中有解释。
英文:

<!-- language-all: sh -->

You're trying to compare folder trees by the relative paths of the files in them.

While Get-ChildItem's -Name parameter does output relative paths, it is all that it outputs, i.e. it emits strings rather than the usual System.IO.FileInfo (and System.IO.DirectoryInfo) instances, which means that the output won't have properties such as .FullName

If the requirement to optionally ignore extension weren't in the picture, you would be able to reconstruct the original full paths, by inferring what root path to prepend, depending on the value of .SideIndicator in the output from Compare-Object.

To satisfy your requirements, you'll have to:

  • Add a .RelativePath property to the [pscustomobject] instances you construct, and manually determine the relative path, by removing the root path's substring from the .FullName property of each file.

  • Compare the arrays of custom objects by that property, using -Property RelativePath. Additionally, in order to ensure that your custom objects are passed through as a whole, you must add -PassThru

To put it all together:

  1. $folder1 = Read-Host &quot;Enter the path for Folder A&quot;
  2. $folder2 = Read-Host &quot;Enter the path for Folder B&quot;
  3. # Prompt for whether to ignore file extensions
  4. $ignoreExtensions = &#39;y&#39; -eq (Read-Host &#39;Ignore file extensions? (y/n)&#39;)
  5. # Get the files in each folder and store their relative and full paths
  6. # in arrays, optionally without extensions.
  7. $dir1Dirs, $dir2Dirs = $folder1, $folder2 |
  8. ForEach-Object {
  9. $fullRootPath = Convert-Path -LiteralPath $_
  10. # Construct the array of custom objects for the folder tree at hand
  11. # and *output it as a single object*, using the unary form of the
  12. # array construction operator, &quot;,&quot;
  13. , @(
  14. Get-ChildItem -File -Recurse -LiteralPath $fullRootPath |
  15. ForEach-Object {
  16. $relativePath = $_.FullName.Substring($fullRootPath.Length + 1)
  17. if ($ignoreExtensions) { $relativePath = $relativePath -replace &#39;\.[^.]*$&#39; }
  18. [PSCustomObject] @{
  19. RelativePath = $relativePath
  20. FullName = $_.FullName
  21. }
  22. }
  23. )
  24. }
  25. # Compare the two arrays.
  26. # Note the use of -Property RelativePath and -PassThru
  27. # as well as the Where-Object SideIndicator -eq &#39;=&gt;&#39; filter, which
  28. # - as in your question - only reports differences
  29. # from the -DifferenceObject collection.
  30. # To report differences from *either* collection, simply remove the filter.
  31. $diff =
  32. Compare-Object -Property RelativePath -PassThru $dir1Dirs $dir2Dirs |
  33. Where-Object SideIndicator -eq &#39;=&gt;&#39;
  34. # Output the results.
  35. if ($diff) {
  36. Write-Host &quot;Files that are different:&quot;
  37. $diff | Select-Object -ExpandProperty FullName
  38. } else {
  39. Write-Host &quot;No differences found.&quot;
  40. }

Note:

  • The use of , @(...) to ensure that the Get-ChildItem output objects are output as an array as a whole (a single object) is explained in this answer.

答案2

得分: 1

你的代码中有两个问题。

  1. 在使用 Compare-Object 时,你还需要提供 -Property 参数来指示要检查的属性值。
  2. 在使用 Get-ChildItem 时,你使用了 -Name 属性,这是不起作用的。

以下是更新后的代码:

  1. # 提示输入要比较的两个文件夹的路径
  2. $folder1 = Read-Host "输入文件夹 A 的路径"
  3. $folder2 = Read-Host "输入文件夹 B 的路径"
  4. # 提示是否忽略文件扩展名
  5. $ignoreExtensions = Read-Host "忽略文件扩展名?(y/n)"
  6. $ignoreExtensions = $ignoreExtensions.ToLower() -eq "y"
  7. # 获取每个文件夹中的文件,并将它们的名称和路径存储在数组中
  8. $dir1Dirs = Get-ChildItem $folder1 -Recurse | ForEach-Object {
  9. $name = $_.Name
  10. if ($ignoreExtensions) {
  11. $name = $name -replace '\.[^.]*$'
  12. }
  13. [PSCustomObject]@{
  14. Name = $name
  15. FullName = $_.FullName
  16. }
  17. }
  18. $dir2Dirs = Get-ChildItem $folder2 -Recurse | ForEach-Object {
  19. $name = $_.Name
  20. if ($ignoreExtensions) {
  21. $name = $name -replace '\.[^.]*$'
  22. }
  23. [PSCustomObject]@{
  24. Name = $name
  25. FullName = $_.FullName
  26. }
  27. }
  28. # 比较两个文件名数组,并显示不同的文件的路径
  29. $diff = Compare-Object -ReferenceObject $dir1Dirs -DifferenceObject $dir2Dirs -Property FullName | Where-Object { $_.SideIndicator -eq "=>" }
  30. if ($diff) {
  31. Write-Host "不同的文件:"
  32. $diff | Select-Object -ExpandProperty FullName
  33. } else {
  34. Write-Host "未发现差异。"
  35. }

希望这有所帮助。

英文:

You were missing two things in your code.

  1. When using Compare-Object you also need to provide the -Property to indicate which property value to check.
  2. When using Get-ChildItem you was using -Name property, which is not going to work.

Here is the updated code:

  1. # Prompt for the paths of the two folders to compare
  2. $folder1 = Read-Host &quot;Enter the path for Folder A&quot;
  3. $folder2 = Read-Host &quot;Enter the path for Folder B&quot;
  4. # Prompt for whether to ignore file extensions
  5. $ignoreExtensions = Read-Host &quot;Ignore file extensions? (y/n)&quot;
  6. $ignoreExtensions = $ignoreExtensions.ToLower() -eq &quot;y&quot;
  7. # Get the files in each folder and store their names and paths in arrays
  8. $dir1Dirs = Get-ChildItem $folder1 -Recurse | ForEach-Object {
  9. $name = $_.Name
  10. if ($ignoreExtensions) {
  11. $name = $name -replace &#39;\.[^.]*$&#39;
  12. }
  13. [PSCustomObject]@{
  14. Name = $name
  15. FullName = $_.FullName
  16. }
  17. }
  18. $dir2Dirs = Get-ChildItem $folder2 -Recurse | ForEach-Object {
  19. $name = $_.Name
  20. if ($ignoreExtensions) {
  21. $name = $name -replace &#39;\.[^.]*$&#39;
  22. }
  23. [PSCustomObject]@{
  24. Name = $name
  25. FullName = $_.FullName
  26. }
  27. }
  28. # Compare the two arrays of file names and display the paths to files that are different
  29. $diff = Compare-Object -ReferenceObject $dir1Dirs -DifferenceObject $dir2Dirs -Property FullName | Where-Object { $_.SideIndicator -eq &quot;=&gt;&quot; }
  30. if ($diff) {
  31. Write-Host &quot;Files that are different:&quot;
  32. $diff | Select-Object -ExpandProperty FullName
  33. } else {
  34. Write-Host &quot;No differences found.&quot;
  35. }

huangapple
  • 本文由 发表于 2023年5月24日 19:18:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76322976.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定