【问题标题】:Get-ChildItem script hangs due to large object?Get-ChildItem 脚本由于大对象而挂起?
【发布时间】:2026-01-05 04:55:02
【问题描述】:

好的 - 我是 PowerShell 的新手。我两周前才开始使用它。我在网上搜索了一些脚本,现在我正在尝试一些看起来有点高级的东西,但我不确定我应该如何解决这个问题。

我正在创建一个审核脚本以确定两个备份存储库之间的哪些文件不同,以确保它们已正确同步(同步脚本使用 robocopy,并且它们多次失败而没有产生错误)。这些文件夹非常广泛,有时我发现脚本只是挂在某些文件夹上(总是在其中最大的文件夹上),因此它永远不会完成。

起初,我在完整的源路径上使用 Get-ChildItem,但这会产生内存问题并且脚本永远无法完成。所以,我想我会枚举子目录并对每个子目录进行比较......但根据文件夹的不同,这也会变得很糟糕。

这是脚本(使用 Powershell 2):

$serverArray=@("Server1","Server2","Server3")

for ($i=0; $i -lt 8; $i++) {

    $server = $serverArray[$i]
    $source="\\$server\Share\"

    $destination = "D:\BackupRepository\$server"
    # Copy to removable drive
    $remoteDestination = "T:\BackupRepository\" + $server

    $log = $server + "ShareBackup.log"
    $remoteLog = "Remote_" + $server + "ShareBackup.log"
    $logDestination = $localLogPath + $log
    $logUNCDestination = $uncLogPath + $log
    $logRemoteDestination = $localLogPath + $remoteLog
    $logUNCRemoteDestination = $uncLogPath + $remoteLog

    ## This file is used for the process of checking
    ## whether or not the backup was successful
    $backupReport = $localReportPath + $server + "ShareBackupReport.txt"
    $remoteBackupReport = $localReportPath + "Remote_" + $server + "ShareBackupReport.txt"

    ## Variables for the failure emails
    $failEmailSubject = "AUDIT REPORT for " + $server
    $failRemoteEmailSubject = "AUDIT REPORT for " + $server
    $failEmailBody = "The Audit for " + $server + " has found a file mismatch. Please consult the attached Backup Report."
    $failRemoteEmailBody = "The Audit of the Remote Backups for " + $server + " has found a file mismatch. Please consult the attached Backup Report."

    $sourceFolderArray = Get-ChildItem $source | ?{ $_.PSIsContainer }
    $sourceFolderCount = $sourceFolderArray.Count
    $mismatchCount = 0
    $remoteMismatchCount = 0

    for ($s1=0; $s1 -lt $sourceFolderCount; $s1++) {

        $sourceFolder = $sourceFolderArray[$s1].FullName
        $sourceFolderName = $sourceFolderArray[$s1].Name
        $destFolder = $destination + "\" + $sourceFolderName
        $remoteDestFolder = $remoteDestination + "\" + $sourceFolderName

        Write-Host "Currently working on: " $sourceFolderName

        $shot1 = Get-ChildItem -recurse -path $sourceFolder
        $shot2 = Get-ChildItem -recurse -path $destFolder
        $shot3 = Get-ChildItem -recurse -path $remoteDestFolder

        $auditReportDest = "C:\BackupReports\Audits\"
        $auditReportOutput = $auditReportDest + $server + "_" + $sourceFolderName + ".txt"
        $auditReportRemoteOutput = $auditReportDest + $server + "_Remote_" + $sourceFolderName + ".txt"
        $auditMismatchReport = $auditReportDest + "MismatchReport_" + $numericDate + ".txt"

        Compare-Object $shot1 $shot2 -PassThru > $auditReportOutput
        Compare-Object $shot2 $shot3 -PassTHru > $auditReportRemoteOutput

        $auditCompare = Get-ChildItem $auditReportOutput
        $auditRemoteCompare = Get-ChildItem $auditReportRemoteOutput

        if ($auditCompare.Length -gt 0) {
            $content = Get-ChildItem -Recurse $auditReportOutput
            Add-Content $auditMismatchReport $content
            Write-Host "Mismatch FOUND: " $sourceFolderName
            $mismatchCount = $mismatchCount + 1
            }
        if ($auditRemoteCompare.Length -gt 0) {
            $remoteContent = Get-ChilItem -Recurse $auditReportRemoteOutput
            Add-Content $auditMismatchReport $remoteContent
            Write-Host "Remote Mismatch FOUND: " $sourceFolderName
            $remoteMismatchCount = $remoteMismatchCount + 1
            }

        }

    send-mailmessage -from $emailFrom -to $emailTo -subject "AUDIT REPORT: Backups" -body "The full mismatch report is attached. There were $mismatchCount mismatched folders found and $remoteMismatchCount remote mismatched folders found. Please review to ensure backups are current." -Attachments "$auditMismatchReport" -priority High -dno onSuccess, onFailure -smtpServer $emailServer

    }

我在交互式运行时发现的是,我会得到一个“当前正在处理 FolderName”,如果该对象“太大”(不管是什么),脚本就会坐在那里,不会给出任何错误的指示,但它不会继续(我已经等了几个小时)。有时我可以交互地按 Ctrl-C 而不是退出脚本,而是将中断作为当前进程的取消并移至下一个项目。

问题是,我需要安排每天执行此操作,以确保备份保持同步。任何帮助或见解表示赞赏。而且,是的,这可能是原始且不雅的,但现在我只是想解决如何绕过挂在我身上的脚本。

【问题讨论】:

  • 你是在检查文件名,还是在检查长度匹配?
  • 只检查文件名以确保它们存在。每天都有足够多的文件变化,这就是我们所需要的。

标签: powershell powershell-2.0


【解决方案1】:

不确定您使用的是哪个版本的 PS,但 Get-Childitem 已知在扩展到大型目录时会出现问题:

http://blogs.msdn.com/b/powershell/archive/2009/11/04/why-is-get-childitem-so-slow.aspx

如果您只是比较文件名,则可以使用旧版 dir 命令在大型目录结构中获得更好的结果。 /b(裸)开关只返回可与 Powershell 的比较运算符一起使用的全名字符串。

$sourcedir = 'c:\testfiles'
$source_regex = [regex]::escape($sourcedir)

(cmd /c dir $Sourcedir /b /s) -replace "$source_regex\\(.+)$",'$1'

这使用正则表达式和 -replace 运算符从 dir 返回的全名中删除 soruce 目录。 -replace 运算符适用于数组,因此您可以在一个操作中完成所有这些操作,而无需 foreach 循环。

【讨论】:

  • 使用 Powershell 2.0 - 抱歉。我应该把那个贴出来的……它在标签里,但那几乎是看不见的。我的错。我会检查 dir... 我没有意识到我可以在递归场景中使用它。
  • 抱歉,我错过了标签。我相信那篇文章中的 gci 性能问题确实适用于 V2。
  • 首先 - 感谢您抽出宝贵时间提供帮助。我很感激。所以,这绝对是进步,问题是因为我正在比较两个完全不同的目录结构, /b 开关给了我完整的路径......所以 Compare-Object 总是产生一个完整的列表(因为由于完整路径没有匹配)。我可以在 Compare-Object 中做些什么来让它忽略基本路径,或者有没有办法让 dir 命令输出除基本路径之外的所有内容?
  • 可以,但是只使用 /b 开关然后使用字符串操作处理结果会更快。我将用一个示例更新脚本。
  • 非常感谢先生!那是为我做的。大目录现在已被证明是没有问题的。标记为答案,但我还没有足够的声望来投票。
最近更新 更多