PowerShell:解析包含大量文件的文件夹的性能

问题描述 投票:0回答:1

我编写了一个脚本来清除可能有 10 到 15 万个文件的存档文件夹中的旧文件 脚本的上下文, 我不知道每个文件夹的深度,但是最后有一个名为“AAAA-MM”的格式化文件夹,并且在每个文件夹中每天都有一个名为“DD”的文件夹,因此解决方案是将文件夹名称连接到约会。 没关系, 但。我可以看到,即使递归没有打开“day 文件夹”,脚本也会生成此错误: PS>TerminateError(Get-ChildItem): "Exception of type 'System.OutOfMemoryException' was throwed." 当它解析文件夹时对于巨大的文件夹,即使它不应该打开或解析或执行任何其他操作,最坏的情况是获取 31 个文件夹的名称

我可能在脚本中犯了一个错误? 也许有办法在每次递归时清空内存?

你能在这一点上帮我吗?

谢谢。

这是我的递归代码

function Recursive-FolderSearch {
    param (
        [string[]]$Paths,  
        [string]$expiredDate  
    )
    
    foreach ($Path in $Paths) {
        $now = Get-Date -format "yyyy-MM-dd HH:mm:ss"
        Write " - $now : $Path" >> $dailyLog
        if ((Test-Path $Path -PathType Container) -and (Split-Path $Path -Leaf) -match '^\d{4}-\d{2}$') {
            
            $folderDays = Get-ChildItem -Path $Path -Directory
            foreach ($folderDay in $folderDays) {
                if ($folderDay.Name -match '^\d{2}$') {# if folder respect format DD
                    # concat folder AAAA-MM and folder format DD to get complete date
                    $dateDossier = [datetime]::ParseExact("$(Split-Path $Path -Leaf)-$($folderDay.Name)", "yyyy-MM-dd", $null)
                    if ($dateDossier -lt $expiredDate) { #compare folder date with actual date 
                    # then suppress folder if older than requested
                        Remove-Item -Path $folderDay.FullName -Recurse -Force -ErrorAction SilentlyContinue
                    }
                }
            }
            #Supprimer le dossier AAAA-MM s'il est vide
            if (-not (Get-ChildItem -Path $Path -Force -Recurse -ErrorAction SilentlyContinue  )) { #| Where-Object { $_. PSIsContainer -eq $true }
                Remove-Item -Path $Path -Force -Recurse -ErrorAction SilentlyContinue
            }
        
        } 
        elseif (Test-Path $Path -PathType Container) {
            $subDirs = Get-ChildItem -Path $Path -Directory -Force -ErrorAction SilentlyContinue

            foreach ($subDir in $subDirs) {
                Recursive-FolderSearch -Paths $subDir.FullName $expiredDate
            }
        } 
    }
}
powershell recursion memory out-of-memory
1个回答
0
投票

不清楚您的代码是否有效,但为了解决

OutOfMemoryException
的问题,您应该更改为使用
Stack<T>
而不是函数递归(脚本块递归),这在 PowerShell 中非常糟糕。

降低内存消耗的另一个改进可以是使用

DirectoryInfo
实例方法进行目录枚举删除

以下是代码的外观,但是建议注释掉这些

.Delete($true)
行和主机输出,以确保代码将删除您要删除的文件夹

function Recursive-FolderSearch {
    param (
        [string[]] $Paths,
        [string] $expiredDate
    )

    $targetDate = Get-Date $expiredDate -ErrorAction Stop
    $stack = [System.Collections.Generic.Stack[System.IO.DirectoryInfo]]::new()
    Get-Item $Paths | ForEach-Object {
        if ($_ -is [System.IO.DirectoryInfo]) {
            $stack.Push($_)
        }
    }

    while ($stack.Count) {
        $dir = $stack.Pop()
        # `$dir` is always `DirectoryInfo`
        # condition `Test-Path $Path -PathType Container` can be removed
        try {
            $children = $dir.EnumerateDirectories()
        }
        catch {
            # leave empty to ignore inaccessible or add error handling here
            continue
        }

        if ($dir.Name -match '^\d{4}-\d{2}$') {
            # we don't know yet if `$dir` has child folders, assume it doesn't
            $hasChild = $false
            foreach ($child in $children) {
                # if we enter this loop we know `$dir` has children
                $hasChild = $true
                if ($child.Name -match '^\d{2}$') {
                    $dateDossier = [datetime]::ParseExact(
                        "$($dir.Name)-$($child.Name)",
                        'yyyy-MM-dd',
                        $null)

                    if ($dateDossier -lt $targetDate) {
                        # compare folder date with actual date
                        # then suppress folder if older than requested
                        $dir.Delete($true)
                    }
                }
            }

            # Supprimer le dossier AAAA-MM s'il est vide
            if (-not $hasChild) {
                $dir.Delete($true)
            }

            continue
        }

        # here is for `$dir` didn't match pattern '^\d{4}-\d{2}$'
        foreach ($child in $children) {
            # we can push these folders
            $stack.Push($child)
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.