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

如何逆序执行Get-ChildItem并优化超50万文件的遍历脚本?

针对大文件夹遍历与冗余文件删除的优化方案

首先解决你最关心的逆序遍历需求,然后再从根本上优化脚本性能——毕竟50万+文件的场景,原脚本的写法确实会导致极端缓慢的执行速度。

一、实现Get-ChildItem逆序遍历

针对大文件夹,推荐两种逆序方式,优先选第一种流式处理的方案(内存更友好):

方案1:使用.NET API流式逆序

Get-ChildItem本质是封装了.NET的DirectoryInfo,直接用[System.IO.Directory]::EnumerateFiles()可以实现流式遍历(不一次性加载所有文件到内存),再通过反转数组实现逆序:

# 流式获取文件路径,逆序遍历
$files = [System.IO.Directory]::EnumerateFiles($RPT)
[Array]::Reverse($files)
foreach ($filePath in $files) {
    $file = Get-Item $filePath
    # 后续逻辑...
}

方案2:Sort-Object配合Get-ChildItem

如果想更简洁,也可以用Sort-Object,但注意大文件夹下Sort-Object需要先加载所有文件到内存,可能占用较多内存:

Get-ChildItem -Path $RPT -File | Sort-Object -Property Name -Descending | ForEach-Object {
    # 后续逻辑...
}

二、脚本性能核心优化(从24小时→数小时的关键)

原脚本的性能瓶颈主要在重复正则编译、低效集合查询、冗余操作这几个点,我们逐一优化:

1. 将数组查询改为哈希表(O(1)查询替代O(n)遍历)

原脚本中$submidlist -match "^$number$"和$jobidlist -match "^$number$"会遍历整个数组判断匹配,50万+循环下来会产生天文数字的遍历次数。改成哈希表查询速度会提升几个数量级:

# 提前将列表转为哈希表,初始化一次即可
$submidHash = @{}
$submidlist | ForEach-Object { $submidHash[[string]$_] = $true }

$jobidHash = @{}
$jobidlist | ForEach-Object { $jobidHash[[string]$_] = $true }

后续判断直接用:

if ($submidHash.ContainsKey($number)) { ... }
if ($jobidHash.ContainsKey($number)) { ... }

2. 预编译正则表达式,避免重复编译

原脚本每次循环都重新解析正则字符串,预编译后复用可以大幅减少CPU消耗:

# 预编译要保留的文件后缀/名称正则
$keepPattern = [regex]::new('acct\.csv|jpd\.csv|jss\.pdf|jman\.pdf|3600\.pdf|cont\.pdf|msl\.txt|pres\.pdf|tray\.pdf|qual\.pdf|zipl\.pdf')
# 预编译提取12位数字的正则
$numberPattern = [regex]::new('\d{12}')

后续判断用:

$isKeepFile = $keepPattern.IsMatch($_.Name)
$match = $numberPattern.Match($_.Name)

3. 减少不必要的输出,降低IO开销

原脚本每次删除都用echo输出,大量的控制台IO会拖慢速度。可以改成批量输出或者用进度条替代:

# 每处理1000个文件更新一次进度条
if ($totalcount % 1000 -eq 0) {
    Write-Progress -Activity "清理冗余文件" -Status "已处理 $totalcount 个文件,已删除 $count 个" -PercentComplete ([math]::Min(($totalcount / 500000) * 100, 100))
}

4. 最终优化后的完整脚本示例

# 初始化参数
$RPT = "你的文件夹路径"
$submidlist = @("你的submid列表")
$jobidlist = @("你的jobid列表")
$totalcount = 0
$count = 0

# 预编译正则和哈希表
$numberPattern = [regex]::new('\d{12}')
$keepPattern = [regex]::new('acct\.csv|jpd\.csv|jss\.pdf|jman\.pdf|3600\.pdf|cont\.pdf|msl\.txt|pres\.pdf|tray\.pdf|qual\.pdf|zipl\.pdf')

$submidHash = @{}
$submidlist | ForEach-Object { $submidHash[[string]$_] = $true }

$jobidHash = @{}
$jobidlist | ForEach-Object { $jobidHash[[string]$_] = $true }

# 逆序流式遍历文件
$files = [System.IO.Directory]::EnumerateFiles($RPT)
[Array]::Reverse($files)

foreach ($filePath in $files) {
    $file = Get-Item $filePath
    $totalcount++

    # 提取12位数字
    $match = $numberPattern.Match($file.Name)
    if (-not $match.Success) {
        continue
    }
    $number = [int]$match.Value
    $isKeepFile = $keepPattern.IsMatch($file.Name)

    # 判断是否删除
    $flag = 0
    if ($submidHash.ContainsKey($number) -and -not $isKeepFile) {
        Write-Host "DELETE SUBMID $($file.Name)"
        Remove-Item -Path $filePath -Force -ErrorAction SilentlyContinue
        $count++
        $flag = 1
    }
    if ($jobidHash.ContainsKey($number) -and $isKeepFile) {
        Write-Host "DELETE JOBID $($file.Name)"
        Remove-Item -Path $filePath -Force -ErrorAction SilentlyContinue
        $count++
        $flag = 1
    }

    # 每1000次更新进度
    if ($totalcount % 1000 -eq 0) {
        Write-Progress -Activity "清理冗余文件" -Status "已处理 $totalcount 个文件,已删除 $count 个" -PercentComplete ([math]::Min(($totalcount / 500000) * 100, 100))
    }
}

Write-Progress -Activity "清理冗余文件" -Completed
Write-Host "清理完成,共处理 $totalcount 个文件,删除 $count 个"

三、额外建议

  • 如果服务器CPU核心足够,可以尝试用ForEach-Object -Parallel并行处理,但注意文件操作的线程安全,建议先小范围测试;
  • 定期清理文件夹,避免文件数量积累到50万+,可以考虑按日期归档旧文件;
  • 用Measure-Command测试脚本的执行时间,对比优化前后的差异,找到进一步优化的空间。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:52:04