【问题标题】:Powershell - get diff of two files without consuming huge amount of memory - is there any alternative c# or c++ API?Powershell - 在不消耗大量内存的情况下获取两个文件的差异 - 是否有任何替代的 c# 或 c++ API?
【发布时间】:2025-12-06 21:40:02
【问题描述】:

我有两个要比较的大文件(超过 10 GB)。以下命令适用于小文件,但似乎占用了我机器上的 RAM 空间。

如何在不消耗大量内存的情况下获得两个文件的差异?

任何想法都将不胜感激。

robocopy.exe C:\Folder\ C:\Folder\ /l /nocopy /is /e /fp /ns /nc /njh /njs /tee  /log:c:\temp\FolderList.txt

$path = 'C:\Folder\'
$pattern = [regex]::Escape($path)
$newContent = @()
Get-Content -Path "c:\temp\FolderList.txt" | ForEach-Object {$newContent += $_ -replace $pattern, ''}
Set-Content -Path "c:\temp\FolderList.txt" -Value $newContent

(Get-Content C:\temp\FolderList.txt).Trim() -ne '' | Set-Content C:\temp\FolderList.txt

robocopy.exe C:\Folder2\ C:\Folder2\ /l /nocopy /is /e /fp /ns /nc /njh /njs /tee  /log:c:\temp\FolderList2.txt

$path = 'C:\Folder2\'
$pattern = [regex]::Escape($path)
$newContent = @()
Get-Content -Path "c:\temp\FolderList2.txt" | ForEach-Object {$newContent += $_ -replace $pattern, ''}
Set-Content -Path "c:\temp\FolderList2.txt" -Value $newContent

(Get-Content C:\temp\FolderList2.txt).Trim() -ne '' | Set-Content C:\temp\FolderList2.txt

Compare-Object -ReferenceObject (Get-Content c:\temp\FolderList.txt) -DifferenceObject (Get-Content c:\temp\FolderList2.txt)

最后更新

文件夹列表.txt

C:\Folder\Data2\Documents\
        C:\Folder\Data2\Documents\1.txt
        C:\Folder\Data2\Documents\2.txt
        C:\Folder\Data2\Documents\3.txt
        C:\Folder\Data2\Documents\4.txt
        C:\Folder\Data2\Documents\5.txt

比较Log1.txt

Data2\Documents\
C:\Folder\Data2\Documents\
        Data2\Documents\1.txt
        C:\Folder\Data2\Documents\1.txt
        Data2\Documents\2.txt
        C:\Folder\Data2\Documents\2.txt
        Data2\Documents\3.txt
        C:\Folder\Data2\Documents\3.txt
        Data2\Documents\4.txt
        C:\Folder\Data2\Documents\4.txt
        Data2\Documents\5.txt
        C:\Folder\Data2\Documents\5.txt

期望的输出:

Data2\Documents\
Data2\Documents\1.txt
Data2\Documents\2.txt
Data2\Documents\3.txt
Data2\Documents\4.txt
Data2\Documents\5.txt

更新-2:

输出:

Data2\Documents\
C:\Folder\Data2\Documents\
Data2\Documents\1.txt
C:\Folder\Data2\Documents\1.txt
Data2\Documents\2.txt
C:\Folder\Data2\Documents\2.txt
Data2\Documents\3.txt
C:\Folder\Data2\Documents\3.txt
Data2\Documents\4.txt
C:\Folder\Data2\Documents\4.txt
Data2\Documents\5.txt
C:\Folder\Data2\Documents\5.txt

【问题讨论】:

  • 为什么不能依赖外部命令行工具,例如默认的 Windows 工具 comp.exe 或 Microsoft 实用程序 WindDiffwinmerge 来获取差异/补丁?
  • 澄清一下,您是不是特别想区分两个 robocopy 日志文件?或者您是否尝试比较两个文件系统文件夹,而 robocopy 正是您尝试实现该目标的途径?
  • 您是否专门尝试区分两个 robocopy 日志文件?是的
  • 嗯,我希望有一种方法可以一次从两个文件流式传输一行,例如 get-content file1,file2 | % { compare-object $_[0] $_[1] },但它不起作用。

标签: performance powershell compare


【解决方案1】:

首先,使用+= 向数组添加东西是一个已知的内存消耗,因为数组具有固定长度,当您向其中添加新元素时,complete 数组需要在内存中重建。

所以对于每个日志文件的替换和删除空行,我建议这样做:

robocopy.exe C:\Folder\ C:\Folder\ /l /nocopy /is /e /fp /ns /nc /njh /njs /tee  /log:c:\temp\FolderList.txt
robocopy.exe C:\Folder2\ C:\Folder2\ /l /nocopy /is /e /fp /ns /nc /njh /njs /tee  /log:c:\temp\FolderList2.txt

$path    = 'C:\Folder\'
$newFile = 'C:\temp\CompareLog_1.txt'  # have it create a new file instead of gathering all 10Gb in memory
$pattern = [regex]::Escape($path)
# use 'switch' to parse the log file line-by-line
# and write the processed lines to the new file.
# this will be lean on mmory, but takes a lot of disk write actions..
switch -Regex -File 'C:\temp\FolderList.txt' {
    $pattern { Add-Content $newFile -Value ($_ -replace $pattern).Trim() }
    default  { if ($_ -match '\S') { Add-Content $newFile -Value $_.Trim() }}  # non-empty or whitespace-only lines
}

对于第二个日志文件:

$path    = 'C:\Folder2\'
$newFile = 'C:\temp\CompareLog_2.txt'
$pattern = [regex]::Escape($path)
switch -Regex -File 'C:\temp\FolderList2.txt' {
    $pattern { Add-Content $newFile -Value ($_ -replace $pattern).Trim() }
    default  { if ($_ -match '\S') { Add-Content $newFile -Value $_.Trim() }}
}

接下来你需要比较CompareLog_1.txtCompareLog_2.txt的新文件,但我猜这些可能仍然很大,所以我同意Zilog80最好使用专用软件。

根据您希望看到的结果,您也可以考虑使用旧的fc.exe,它运行速度快且不占用内存。
类似的东西

fc.exe  /C /N 'C:\temp\CompareLog_1.txt' 'C:\temp\CompareLog_2.txt'

您可以通过不使用Add-Content,而是使用StreamWriter来加快写入要比较的文件: (这将创建一个 Utf8NoBOM 编码的文件)

$path    = 'C:\Folder\'
$newFile = 'C:\temp\CompareLog_1.txt'
$writer  = [System.IO.StreamWriter]::new($newFile)
$pattern = [regex]::Escape($path)
switch -Regex -File 'C:\temp\FolderList.txt' {
    $pattern { $writer.WriteLine(($_ -replace $pattern).Trim()) }
    default  { if ($_ -match '\S') { $writer.WriteLine($_.Trim()) }}
}
# clean up
$writer.Flush()
$writer.Dispose()

$path    = 'C:\Folder2\'
$newFile = 'C:\temp\CompareLog_2.txt'
$writer  = [System.IO.StreamWriter]::new($newFile)
$pattern = [regex]::Escape($path)
switch -Regex -File 'C:\temp\FolderList2.txt' {
    $pattern { $writer.WriteLine(($_ -replace $pattern).Trim()) }
    default  { if ($_ -match '\S') { $writer.WriteLine($_.Trim()) }}
}
# clean up
$writer.Flush()
$writer.Dispose()

【讨论】:

  • 好的,谢谢,但是修剪功能不适用于您的脚本。我已经更新了我的问题。 (5/11)
  • 我已经尝试过 StreamWriter: 和你的第一个脚本。但没有运气。顺便说一句,我的原始脚本确实正确地修剪了它。
  • @Arbelac Ah.. 错过了 Trim()。现在添加了
  • 还是一样 :) 我添加了我的输出,叫做 UPDATE -2
  • 有什么意见吗?
最近更新 更多