You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

大量文件目录下PowerShell搜索函数运行缓慢的优化咨询

PowerShell 大文件目录搜索提速方案求助

我在指定Region文件夹内按规则搜索文件并展示,但当目录包含大量文件(如200k个)时,搜索耗时约10分钟,文件量少时速度正常。尝试使用Runspaces优化但未成功,耗时无变化。当前使用PowerShell版本5.1.22621.4249,以下是我的初始实现代码、尝试Runspaces的代码及已实现缓存功能的代码,寻求有效的搜索提速方案:

初始实现代码

function Convert-FileName { 
    param (
        [string]$fileName
    )

    $fileName = $fileName.TrimStart('.')

    if ($fileName -match "^(\d+)\.([A-Z]+)\.01\.(\d+)\.(\d{8})$") {
        return @{
            "PayerID"    = $matches[1]
            "Region"     = $matches[2]
            "CheckNum"   = $matches[3]
            "CheckDate"  = [datetime]::ParseExact($matches[4], "yyyyMMdd", $null)
        }
    } else {
        return $null
    }
}

$buttonSearch.Add_Click({
    $region = $comboBoxRegion.Text
    $payIDRange = $textBoxPayID.Text
    $checkNumRange = $textBoxCheckNum.Text
    $checkDateRange = $textBoxCheckDate.Text

    if ([string]::IsNullOrWhiteSpace($global:selectedFolder)) {
        [System.Windows.Forms.MessageBox]::Show("Please choose a folder before searching.")
        return
    }

    if ([string]::IsNullOrWhiteSpace($region)) {
        $dataGridView.Rows.Clear()
        [System.Windows.Forms.MessageBox]::Show("Please select a region.")
        return
    }

    if ([string]::IsNullOrWhiteSpace($payIDRange) -or $payIDRange -notmatch "^\d+-\d+$") {
        $dataGridView.Rows.Clear()
        [System.Windows.Forms.MessageBox]::Show("Invalid Pay ID range.")
        return
    }

    $global:totalResults = New-Object System.Collections.Generic.List[object]

    $payIDStart, $payIDEnd = $payIDRange -split "-", 2
    $payIDStart = [int]$payIDStart
    $payIDEnd = [int]$payIDEnd

    $allFiles = Get-ChildItem -Path "$global:selectedFolder\$region" -File 

    foreach ($file in $allFiles) {
        $fileInfo = Convert-FileName -fileName $file.Name
        if ($fileInfo -ne $null) {
            $extractedPayerID = [int]$fileInfo.PayerID
            $payIDValid = $extractedPayerID -ge $payIDStart -and $extractedPayerID -le $payIDEnd

            $checkNumValid = $true
            if ($checkNumRange -ne "") {
                $checkNumStart, $checkNumEnd = $checkNumRange -split "-", 2
                $checkNumValid = $fileInfo.CheckNum -ge [int]$checkNumStart -and $fileInfo.CheckNum -le [int]$checkNumEnd
            }

            $checkDateValid = $true
            if ($checkDateRange -ne "") {
                $startDate, $endDate = $checkDateRange -split " - ", 2
                $startDate = [datetime]::ParseExact($startDate.Trim(), "yyyy-MM-dd", $null)
                $endDate = [datetime]::ParseExact($endDate.Trim(), "yyyy-MM-dd", $null)
                $checkDateValid = $fileInfo.CheckDate -ge $startDate -and $fileInfo.CheckDate -le $endDate
            }

            if ($payIDValid -and $checkNumValid -and $checkDateValid) {
                $resultObject = New-Object -TypeName PSObject -Property @{
                    Name = $file.Name
                    Path = $file.FullName
                }
                $global:totalResults.Add($resultObject)
            }
        }
    }

    if ($global:totalResults.Count -eq 0) {
        $dataGridView.Rows.Clear()
        [System.Windows.Forms.MessageBox]::Show("No results found.")
        return
    }

    $global:currentPage = 0
    Show-Results
})

尝试Runspaces优化的代码

function Search-Files {
    Param(
        [string]$selectedFolder,
        [string]$region,
        [string]$payIDRange,
        [string]$checkNumRange,
        [string]$checkDateRange
    )

    $payIDStart, $payIDEnd = $payIDRange -split "-"
    $payIDStart = [int]$payIDStart
    $payIDEnd = [int]$payIDEnd

    $allFiles = Get-ChildItem -Path "$selectedFolder\$region" -File

    $file_count = $allFiles.Count
    $cpu_count = [Environment]::ProcessorCount
    $optimal_runspaces = [Math]::Min($cpu_count, $file_count)
    $file_chunks = [System.Collections.Generic.List[object[]]]::new($optimal_runspaces)

    $runspace_pool = [runspacefactory]::CreateRunspacePool(1, $optimal_runspaces)
    $runspace_pool.Open()

    $chunk_size = [Math]::Ceiling($file_count / $optimal_runspaces)
    for ($i = 0; $i -lt $optimal_runspaces; $i++) {
        $start = $i * $chunk_size
        $end = [Math]::Min(($start + $chunk_size - 1), ($file_count - 1))

        $file_chunks.Add($allFiles[$start..$end])
    }

    $scriptblock = {
        Param($files, $payIDStart, $payIDEnd, $checkNumRange, $checkDateRange)
        function Convert-FileName { 
            param (
                [string]$fileName
            )
        
            $fileName = $fileName.TrimStart('.')
        
            if ($fileName -match "^(\d+)\.([A-Z]+)\.01\.(\d+)\.(\d{8})$") {
                return @{
                    "PayerID"    = $matches[1]
                    "Region"     = $matches[2]
                    "CheckNum"   = $matches[3]
                    "CheckDate"  = [datetime]::ParseExact($matches[4], "yyyyMMdd", $null)
                }
            } else {
                return $null
            }
        }

        $results = [System.Collections.Generic.List[object]]::new()
        foreach ($file in $files) {
            $fileInfo = Convert-FileName $file.Name
            if ($fileInfo -eq $null) { continue }

            $extractedPayerID = [int]$fileInfo.PayerID.Trim()
            if ($extractedPayerID -lt $payIDStart -or $extractedPayerID -gt $payIDEnd) { continue }

            $checkNumValid = $true
            if ($checkNumRange -ne "") {
                $checkNumStart, $checkNumEnd = $checkNumRange -split "-"
                $checkNumValid = ($fileInfo.CheckNum -ge [int]$checkNumStart -and $fileInfo.CheckNum -le [int]$checkNumEnd)
            }

            $checkDateValid = $true
            if ($checkDateRange -ne "") {
                $startDate, $endDate = $checkDateRange -split " - "
                $startDate = [datetime]::Parse($startDate)
                $endDate = [datetime]::Parse($endDate)
                $checkDateValid = ($fileInfo.CheckDate -ge $startDate -and $fileInfo.CheckDate -le $endDate)
            }

            if ($checkNumValid -and $checkDateValid) {
                $results.Add($file)
            }
        }
        return $results
    }

    $runspaces = @{}
    foreach ($chunk in $file_chunks) {
        $runspace = [powershell]::Create().AddScript($scriptblock).AddArgument($chunk).AddArgument($payIDStart).AddArgument($payIDEnd).AddArgument($checkNumRange).AddArgument($checkDateRange)
        $runspace.RunspacePool = $runspace_pool
        $runspaces[$runspace] = $runspace.BeginInvoke()
    }

    while ($runspaces.Values | Where-Object { -not $_.IsCompleted }) {
        Start-Sleep -Milliseconds 100
    }

    $all_results = @()
    foreach ($runspace in $runspaces.Keys) {
        $all_results += $runspace.EndInvoke($runspaces[$runspace])
        $runspace.Dispose()
    }

    $runspace_pool.Close()
    $runspace_pool.Dispose()

    return $all_results
}

$buttonSearch.Add_Click({
    $region = $comboBoxRegion.Text
    $payIDRange = $textBoxPayID.Text
    $checkNumRange = $textBoxCheckNum.Text
    $checkDateRange = $textBoxCheckDate.Text

    if ($global:selectedFolder -eq "") {
        [System.Windows.Forms.MessageBox]::Show("Please choose a folder before searching.")
        return
    }

    if ($region -eq "") {
        $dataGridView.Rows.Clear()
        [System.Windows.Forms.MessageBox]::Show("Please select a region.")
        return
    }

    if ($payIDRange -eq "" -or $payIDRange -notmatch "^\d+-\d+$") {
        $dataGridView.Rows.Clear()
        [System.Windows.Forms.MessageBox]::Show("Invalid Pay ID range.")
        return
    }

    $results = Search-Files -selectedFolder $global:selectedFolder -region $region -payIDRange $payIDRange -checkNumRange $checkNumRange -checkDateRange $checkDateRange

    if ($results.Count -eq 0) {
        $dataGridView.Rows.Clear()
        [System.Windows.Forms.MessageBox]::Show("No results found.")
        return
    }

    $global:totalResults = $results

    $global:currentPage = 0
    Show-Results
})

已实现缓存功能的代码

$global:Cache = @{}
$buttonSearch.Add_Click({
    $region = $comboBoxRegion.Text
    $payIDRange = $textBoxPayID.Text
    $checkNumRange = $textBoxCheckNum.Text
    $checkDateRange = $textBoxCheckDate.Text

    if ([string]::IsNullOrWhiteSpace($global:selectedFolder)) {
        [System.Windows.Forms.MessageBox]::Show("Please choose a folder before searching.")
        return
    }

    if ([string]::IsNullOrWhiteSpace($region)) {
        $dataGridView.Rows.Clear()
        [System.Windows.Forms.MessageBox]::Show("Please select a region.")
        return
    }

    if ([string]::IsNullOrWhiteSpace($payIDRange) -or $payIDRange -notmatch "^\d+-\d+$") {
        $dataGridView.Rows.Clear()
        [System.Windows.Forms.MessageBox]::Show("Invalid Pay ID range.")
        return
    }

    $global:totalResults = New-Object System.Collections.Generic.List[object]

    $payIDStart, $payIDEnd = $payIDRange -split "-", 2
    $payIDStart = [int]$payIDStart
    $payIDEnd = [int]$payIDEnd

    $Directory = $global:selectedFolder + '\' + $region
    $allFiles = [System.IO.Directory]::EnumerateFiles($Directory, '*', [System.IO.SearchOption]::AllDirectories)
    $startDate = $null
    $endDate = $null
    
    if (![string]::IsNullOrWhiteSpace($checkDateRange)) {
        $dateParts = $checkDateRange -split " - ", 2
        if ($dateParts.Length -eq 2) {
            try {
                $startDate = [datetime]::ParseExact($dateParts[0].Trim(), "yyyy-MM-dd", $null)
                $endDate = [datetime]::ParseExact($dateParts[1].Trim(), "yyyy-MM-dd", $null)
            } catch {
                Write-Host "Error parsing dates: '$($dateParts[0].Trim())' or '$($dateParts[1].Trim())'. Please ensure they are in 'yyyy-MM-dd' format."
                return
            }
        }
    }

    foreach ($filePath in $allFiles) {
        $file = New-Object System.IO.FileInfo $filePath
        $currentCacheKey = Generate-CacheKey -fileName $file.FullName -payIDRange $payIDRange -checkNumRange $checkNumRange -checkDateRange $checkDateRange
        
        if (-not $global:Cache.ContainsKey($currentCacheKey)) {
            $fileInfo = Convert-FileName -fileName $file.Name
            if ($fileInfo -ne $null) {
                $extractedPayerID = [int]$fileInfo.PayerID
                $payIDValid = $extractedPayerID -ge $payIDStart -and $extractedPayerID -le $payIDEnd
    
                $checkNumValid = $true
                if ($checkNumRange -ne "") {
                    $checkNumStart, $checkNumEnd = $checkNumRange -split "-", 2
                    $checkNumValid = $fileInfo.CheckNum -ge [int]$checkNumStart -and $fileInfo.CheckNum -le [int]$checkNumEnd
                }
    
                $checkDateValid = $true
                if ($checkDateRange -ne "") {
                    $checkDateValid = $fileInfo.CheckDate -ge $startDate -and $fileInfo.CheckDate -le $endDate
                }
    
                if ($payIDValid -and $checkNumValid -and $checkDateValid) {
                    $resultObject = [PSCustomObject] @{
                        Name = $file.Name
                        Path = $file.FullName
                    }
                    $global:totalResults.Add($resultObject)
                    $global:Cache[$currentCacheKey] = $resultObject
                } else {
                    $global:Cache[$currentCacheKey] = $null
                }
            } else {
                $global:Cache[$currentCacheKey] = $null
            }
        } else {
            $resultObject = $global:Cache[$currentCacheKey]
            if ($resultObject) {
                $global:totalResults.Add($resultObject)
            }
        }
    }

    if ($global:totalResults.Count -eq 0) {
        $dataGridView.Rows.Clear()
        [System.Windows.Forms.MessageBox]::Show("No results found.")
        return
    }

    $global:currentPage = 0
    Show-Results
})

内容的提问来源于stack exchange,提问作者k5927211

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 02:44:50