记一次 18W+ 项目依赖残留撑爆 Win10 回收站导致死机的抢救经历

分类记录
标签AI生成PC
记一次 18W+ 项目依赖残留撑爆 Win10 回收站导致死机的抢救经历
本文使用 MCP 工具自动创建,内容根据作者真实经历,由 Gemini 3.8 Flash 整理生成。

前言:一次寻常的文件整理与意外「死机」

日常做开发项目、拉取依赖(例如前端的 node_modules、各种构建缓存等),日积月累下来可能会产生海量的碎小文件。今天(2026-10-06)我在 Windows 10 电脑中按部就班地整理磁盘空间,顺手将几个很久不用的旧项目文件夹扔进了回收站。

本以为这只是一次普通的右键「清空回收站」,没想到系统资源管理器瞬间无响应,CPU 占用率直接飙升并出现异常锁死,鼠标指针打转,整台电脑随即陷入卡死状态,连任务管理器都难以唤醒。

打开回收站属性排查后令人倒吸一口凉气:回收站里密密麻麻挤满了项目依赖残留,文件总数高达 18W+!Windows 自带的图形界面回收站在面对这种数十万量级的「小文件炸弹」时,单线程扫描和索引开销直接把 CPU 与 I/O 资源榨干。

破局:彻底删除 $Recycle.Bin 目录的 PowerShell 脚本

硬撑着右键清空显然不可行,直接手动删除系统回收站根目录又容易引发权限问题与 GUID 混乱。在反复尝试后,得到了一个更加彻底的方案:不再逐文件分批枚举,而是直接以管理员权限逐盘移除 $Recycle.Bin 目录,遇到权限不足时自动接管所有权并修复 ACL,Windows 会在后续需要时自动重建回收站,安全干净。

核心思路

  • 自动提权: 脚本启动时自动检测当前是否为管理员权限,若不是则通过 UAC 重新以管理员身份启动自身,无需手动右键「以管理员身份运行」。

  • 自动枚举磁盘: 通过 Win32_LogicalDisk 自动获取所有本地固定磁盘(DriveType=3)和可移动磁盘(DriveType=2),不再硬编码盘符,盘符排序后逐个处理。

  • 直接删除 + 权限修复兜底: 首先调用 cmd.exe /c rd /s /q 直接移除 $Recycle.Bin 目录(系统级删除命令,速度极快);若首次删除未完成,则依次调用 takeown 接管所有权、icacls 为 Administrators 组授予完全控制权限,再次重试删除。

  • 安全校验: 处理前严格校验路径,确保只操作盘符根目录下的 $Recycle.Bin,避免路径拼接异常导致误删。

  • 进度与日志: 通过 Write-Progress 显示整体进度,所有操作写入 %TEMP% 下带时间戳的日志文件,最终输出成功/跳过/失败汇总。

完整脚本代码

#requires -Version 5.1
# Windows 10 - 清空所有本地固定磁盘及可移动磁盘的回收站
# 警告:永久删除回收站内容,不可恢复

$ErrorActionPreference = 'Continue'

# ---------- 1. 自动请求管理员权限 ----------
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($identity)
$isAdmin = $principal.IsInRole(
    [Security.Principal.WindowsBuiltInRole]::Administrator
)

if (-not $isAdmin) {
    if (-not $PSCommandPath) {
        Write-Host "请将脚本保存为 .ps1 文件后运行。" -ForegroundColor Red
        Read-Host "按回车退出"
        exit 1
    }

    $argsList = "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`""
    try {
        Start-Process -FilePath "powershell.exe" `
            -ArgumentList $argsList -Verb RunAs
    }
    catch {
        Write-Host "无法获取管理员权限:$($_.Exception.Message)" `
            -ForegroundColor Red
        Read-Host "按回车退出"
    }
    exit
}
// [!code fold:start]
# ---------- 2. 创建日志 ----------
$logFile = Join-Path $env:TEMP (
    "RecycleBinCleanup_{0:yyyyMMdd_HHmmss}.log" -f (Get-Date)
)

function Write-Log {
    param([string]$Message)

    $line = "{0:yyyy-MM-dd HH:mm:ss} {1}" -f (Get-Date), $Message
    Write-Host $line
    Add-Content -LiteralPath $logFile -Value $line -Encoding UTF8
}

Write-Log "========== 开始清理回收站 =========="
Write-Log "日志文件:$logFile"

# ---------- 3. 获取本地固定盘和可移动盘 ----------
try {
    $drives = @(
        Get-CimInstance Win32_LogicalDisk |
        Where-Object { $_.DriveType -in @(2, 3) } |
        Sort-Object DeviceID
    )
}
catch {
    Write-Log "获取磁盘列表失败:$($_.Exception.Message)"
    Read-Host "按回车退出"
    exit 1
}

if ($drives.Count -eq 0) {
    Write-Log "没有发现可处理的本地磁盘。"
    Read-Host "按回车退出"
    exit 0
}

$success = 0
$failed = 0
$skipped = 0
$total = $drives.Count
$index = 0

# ---------- 4. 逐磁盘清理 ----------
foreach ($drive in $drives) {
    $index++
    $letter = $drive.DeviceID
    $recycleBin = Join-Path ($letter + '\') '$Recycle.Bin'

    Write-Progress `
        -Activity "清理 Windows 回收站" `
        -Status "正在处理 $letter ($index/$total)" `
        -PercentComplete (($index / $total) * 100)

    Write-Log "正在处理磁盘 $letter"

    if (-not (Test-Path -LiteralPath $recycleBin -PathType Container)) {
        Write-Log "$letter 没有回收站目录,跳过。"
        $skipped++
        continue
    }

    # 安全检查:只允许处理盘符根目录下的 $Recycle.Bin
    $expected = $letter + '\$Recycle.Bin'
    if ($recycleBin -ine $expected) {
        Write-Log "路径安全检查未通过,跳过:$recycleBin"
        $failed++
        continue
    }

    # 第一次尝试直接删除,避免不必要的全盘权限扫描
    $output = & "$env:SystemRoot\System32\cmd.exe" `
        /d /c "rd /s /q `"$recycleBin`"" 2>&1
    $exitCode = $LASTEXITCODE

    if ($exitCode -ne 0 -or
        (Test-Path -LiteralPath $recycleBin -PathType Container)) {

        Write-Log "$letter 首次清理未完成,尝试修复权限。"

        # 获取目录及其子项的所有权
        $output = & "$env:SystemRoot\System32\takeown.exe" `
            /f $recycleBin /r /d Y 2>&1
        $takeownCode = $LASTEXITCODE

        if ($takeownCode -ne 0) {
            Write-Log "$letter 获取所有权时出现错误:$($output -join ' ')"
        }

        # 向内置 Administrators 组授予完全控制权限
        $output = & "$env:SystemRoot\System32\icacls.exe" `
            $recycleBin /grant '*S-1-5-32-544:(OI)(CI)F' `
            /t /c /q 2>&1
        $aclCode = $LASTEXITCODE

        if ($aclCode -ne 0) {
            Write-Log "$letter 修改权限时出现错误:$($output -join ' ')"
        }

        # 再次尝试清理
        $output = & "$env:SystemRoot\System32\cmd.exe" `
            /d /c "rd /s /q `"$recycleBin`"" 2>&1
        $exitCode = $LASTEXITCODE
    }

    # ---------- 5. 验证结果 ----------
    if (-not (Test-Path -LiteralPath $recycleBin -PathType Container)) {
        Write-Log "$letter 回收站目录已清除。"
        $success++
    }
    else {
        Write-Log "$letter 清理未能完成,可能有文件占用或权限限制。"
        if ($output) {
            Write-Log "最后的命令输出:$($output -join ' ')"
        }
        $failed++
    }
}

Write-Progress -Activity "清理 Windows 回收站" -Completed

# ---------- 6. 输出汇总 ----------
Write-Log "========== 清理结束 =========="
Write-Log "清理成功:$success 个磁盘"
Write-Log "跳过:$skipped 个磁盘"
Write-Log "失败或未完成:$failed 个磁盘"
Write-Log "日志位置:$logFile"

Write-Host ""
Write-Host "清理任务结束。" -ForegroundColor Cyan
Write-Host "成功:$success    跳过:$skipped    失败:$failed"
Write-Host "日志:$logFile"
Write-Host "注意:Windows 通常会在以后需要时重新创建回收站目录。"
Read-Host "按回车退出"

使用方法与清理实录

将脚本保存为 .ps1 文件后直接运行即可,脚本会自动弹出 UAC 提权请求。终端与日志会实时显示每一块磁盘的处理进度:

脚本自动检测权限,若不是管理员则通过 UAC 重新以管理员身份启动自身;随后在 %TEMP% 目录下生成带时间戳的日志文件。通过 CIM 自动扫描所有本地固定盘和可移动盘,按盘符排序;若某个盘符下不存在 $Recycle.Bin 目录则直接跳过。对每个磁盘先尝试 rd /s /q 直接删除回收站目录;如遇权限不足,自动调用 takeown 接管所有权、icacls 授予 Administrators 完全控制权限后重试。所有磁盘处理完毕后,控制台与日志输出成功/跳过/失败的盘数汇总,Write-Progress 进度条结束。依赖系统底层 rd /s /q 命令直删目录,避开了资源管理器与 PowerShell 逐文件枚举的开销,即使面对 18W+ 小文件炸弹也能快速完成,不会再出现 CPU 占满、界面卡死的情况。

给 Windows 10 用户的几点避坑建议

1. 警惕开发目录删除习惯: 无论是 node_modules、vendor 还是虚拟环境 .venv,删除前最好使用 Shift + Delete 永久删除,或者直接通过命令行 rimraf / rd /s /q,千万不要随手拖入回收站。
2. 定期巡检回收站文件数: 不要只看回收站占用的磁盘体积,小文件的数量(inode/元数据)才是引发资源管理器卡顿的元凶。
3. 卡死时切忌反复强制重启: 如果已经触发了清空卡死,切勿强行断电,可以尝试调出任务管理器结束 explorer.exe 进程,然后运行上面的脚本自动清理。
4. 无需担心回收站被删: $Recycle.Bin 是系统受保护目录,删除后 Windows 会在下次需要时自动重建,不会影响系统正常运行。

希望这次险些被「小文件炸弹」干翻的真实经历与解决方案,能给同样在 Win10 环境下做开发运维的小伙伴们提供一些参考与帮助。