1. 为什么开发者需要临时文件自动化清理工具
做开发的人,电脑里总会不知不觉攒下一堆垃圾。今天编译个项目,明天装个依赖包,后天更新一下系统,C盘空间就这么一点一点被啃掉了。我前阵子公司发了台新电脑,512G固态,我寻思怎么也够用了吧,结果不到三个月,C盘爆红。点开一看,光是临时文件、各种缓存、Windows更新残留就占了几十个G。
以前清理这些垃圾靠什么?靠手动。打开资源管理器,地址栏敲%TEMP%,全选删除。运气好能删掉几个G,运气不好直接提示"文件正在使用"或者"需要管理员权限",然后搁那干瞪眼。更闹心的是,有些第三方清理工具装了之后,要么捆绑一堆推广软件,要么后台偷偷扫描,动不动还误删了不该删的东西。后来我实在受不了,决定自己写一个信得过的临时文件自动化工具。
这个工具的核心能力就三件事:自动扫描系统里的临时文件、Windows更新缓存、软件日志、无效缓存和回收站冗余文件;跳过个人文档、照片、已安装软件等关键数据;清理完成后告诉你到底释放了多少空间。听起来简单,但真正落地的时候,里面藏着一堆细节坑。这篇就把我自己的实现思路、完整脚本、踩过的坑全部摊开讲,想自己动手搞一套的开发者可以直接抄作业。
2. 先把临时文件的家底摸清楚
写清理工具之前,你得先知道你电脑里的垃圾到底存在哪些地方。这一步没搞明白,后面写得再花哨也没用。
2.1 开发者最常见的临时文件分布图
Windows系统里的临时文件大致分三类:用户级临时目录、系统级临时目录、应用缓存目录。开发者电脑上还要额外加上各种开发工具和包管理器的缓存目录。
我把常见的清理目标整理成了一张表,方便对照着看:
| 目录位置 | 里面装的是什么 | 能不能删 | 注意事项 |
|---|---|---|---|
%TEMP% | 用户临时文件,软件安装解压、程序运行中间产物 | 可以 | 删除前最好关闭正在运行的软件 |
C:\Windows\Temp | 系统临时文件,多数来自系统组件和更新程序 | 可以 | 需要管理员权限 |
C:\Windows\SoftwareDistribution\Download | Windows更新缓存安装包 | 可以 | 删除后对应更新包需要重新下载 |
C:\Windows\Prefetch | 系统预读文件,加速程序启动用的 | 不建议频繁删 | 删除后启动速度会短暂下降 |
| 回收站 | 已删除文件的暂存区 | 可以 | 清空后不可恢复 |
%LOCALAPPDATA%\Microsoft\Windows\INetCache | IE内核浏览器缓存 | 可以 | 无特殊影响 |
%LOCALAPPDATA%\Google\Chrome\User Data\Default\Cache | Chrome浏览器缓存 | 可以 | 有同名进程占用时删除会失败 |
%LOCALAPPDATA%\npm-cache | npm构建缓存 | 可以 | 删除后下次安装依赖会重新下载 |
%LOCALAPPDATA%\pip\cache | pip包缓存 | 可以 | 同理重新下载 |
%USERPROFILE%\.nuget\packages | NuGet包缓存 | 可以,但不建议全删 | 全删后重新构建会特别慢 |
%USERPROFILE%\.gradle\caches | Gradle构建缓存 | 可以 | 按时间策略删比较安全 |
这张表里的目录,是大多数清理工具脚本里都会出现的"常客"。如果你是开发Go、Java、Node、Python项目的,大概率还能往里面加上%USERPROFILE%\go\pkg\mod、%USERPROFILE%\.m2\repository这类依赖缓存目录。
2.2 哪些临时文件千万不能碰
清理脚本最怕的不是删不干净,而是乱删。有几个位置特别容易踩雷,我吃了不少亏才记住。
第一,C:\Windows\System32和C:\Windows\SysWOW64这两个目录里,有很多动态链接库文件看起来像临时文件,但它们都是系统核心组件。曾经有网友拿通配符直接清理 System32 下的 dll,结果系统直接蓝屏。这属于自杀式清理,脚本里必须把系统目录彻底排除掉。
第二,C:\Windows\Prefetch这个目录,很多教程说删了能提速,实际上它里面是系统预读文件,目的是让你常用软件启动更快。删完之后,启动速度不升反降,尤其是刚删完的那阵子,每次开个软件都明显感觉卡一下。
第三,Office、Photoshop、数据库这些软件的临时文件,有的其实是"未保存文档"的草稿区。比如 Word 在编辑时会在临时目录里保存自动恢复文件,如果清理时机不对,文档没保存的内容可能就找不回来了。同样,PostgreSQL、MySQL 在运行期间会有事务日志文件,删了可能导致数据库异常。这类文件必须靠白名单和进程检查来保护。
第四,正在被进程占用的文件绝对不能硬删。最简单的例子,Chrome 开着的时候,它的缓存目录里有一堆文件被进程锁住,你手动del就会报"另一个程序正在使用此文件"。
3. 自动化工具的核心设计思路:先想清楚再动手
我见过不少人写清理脚本,上来就是del /q /s %TEMP%\*这种一行流。运气好能跑通,运气不好整个用户目录都被删掉一部分,而且根本无人知晓到底删了什么。这种工具最大的问题在于没有设计,只是暴力命令的堆砌。
3.1 安全优先:宁可少删,不可乱删
任何临时文件清理工具,第一设计原则都应该是"删得准,而不是删得多"。我的做法是把整个清理过程拆成四个独立阶段:
- 扫描阶段:遍历所有目标目录,统计每个目录下文件的数量和总大小,但绝对不做任何删除操作。
- 筛选阶段:自动排除正在被占用的文件、系统核心文件、以及白名单里的路径。
- 确认阶段:把清理清单和预计释放空间展示给用户,拿到明确确认后才继续。
- 执行阶段:执行删除,然后对比清理前后的大小,算出真实释放的空间。
这四个阶段缺一不可。尤其是扫描和确认这两步,很多脚本为了追求"一键化"直接跳过了,结果就是用户压根不知道这个工具到底会碰哪些文件。我的经验是,哪怕你是给自己用的工具,也应该保留确认这一步,因为你会慢慢往里面加新的清理规则,没有确认环节,哪天加错了规则都没人拦你。
3.2 技术选型:为什么不用bat,而是PowerShell
清理工具用什么语言写?我最初用的是bat批处理,但后来踩了好几个坑,果断换成了PowerShell。
bat脚本的问题很明显:错误处理几乎等于零,一个字符写错就可能执行出完全不同的命令。比如%path%和%Path%在不同系统上展开结果不一样,带着这种隐患的清理脚本跑起来就跟抽盲盒一样。而且bat对于Unicode路径支持也很差,你的用户名或者软件安装路径里一旦有中文、空格,很容易出问题。
PowerShell的好处是面向对象的,路径处理、文件筛选、错误捕获都要优雅得多。最关键的是,Windows自带的PowerShell不需要额外安装运行环境,你随便找一台Windows机器都能跑。Python也好,但是很多开发机上不一定装了Python,装完了还要处理依赖,对一个个人工具来说太重了。所以我的选择是PowerShell 5.1,Win10及以上系统直接自带的那个版本,兼容性最好。
3.3 清理算法:按时间策略而不是一刀切
清理临时文件最怕的就是"一刀切"。比如%TEMP%目录里,刚创建的文件很可能是某个程序正在使用的,你直接全删,程序可能就崩了。合理的做法是按文件创建时间分档。
我采取的策略是:对%TEMP%和C:\Windows\Temp,只删除创建时间超过7天的文件。这样正在使用的临时文件因为创建时间新,会自然被跳过。对Windows更新缓存目录,则可以设置为全清空,因为这个目录里的文件都是可重新下载的安装包,留不留都无所谓。对回收站,清空之前加一道检查即可。
包管理器的缓存则要更温柔一点,比如npm缓存、pip缓存,设置成删除30天以上未使用的文件,这样既能腾出空间,又不会让你下一次构建时因为缓存全空而重新下载好几个G的依赖包,严重影响效率。
4. 完整实操:从零写一个可落地的清理工具
理论部分说了这么多,直接进入正题。下面是我经过实际调试、在几台不同的开发机上跑过的脚本拆解,你可以直接复制下来改一改就能用。
4.1 第一步:定义清理目标和清理策略
脚本开头,先把所有要扫描的目录、以及每个目录对应的清理策略定义好。我习惯用哈希表数组来管理,后续增删条目只需要改一行。
# 清理目标配置 $cleanupTargets = @( @{ Name = "用户临时目录"; Path = "$env:TEMP"; MaxAgeDays = 7 }, @{ Name = "系统临时目录"; Path = "C:\Windows\Temp"; MaxAgeDays = 7 }, @{ Name = "Windows更新缓存"; Path = "C:\Windows\SoftwareDistribution\Download"; MaxAgeDays = 0 }, @{ Name = "IE/Edge浏览器缓存"; Path = "$env:LOCALAPPDATA\Microsoft\Windows\INetCache"; MaxAgeDays = 7 }, @{ Name = "Chrome缓存"; Path = "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Cache"; MaxAgeDays = 7 }, @{ Name = "npm缓存"; Path = "$env:LOCALAPPDATA\npm-cache"; MaxAgeDays = 30 }, @{ Name = "pip缓存"; Path = "$env:LOCALAPPDATA\pip\cache"; MaxAgeDays = 30 } )这里MaxAgeDays是清理策略里最重要的一个参数。当值为0时,表示清空整个目录;当值大于0时,只删除创建时间超过这个天数的文件。如果你用了一些其他开发工具,比如 Yarn、pnpm、Gradle、Maven,按同样格式往数组里加一行就行。
4.2 第二步:写一个可靠的目录大小统计函数
整个清理工具里,扫描部分其实最消耗时间,也最容易出错。因为目录里可能嵌套着深层子目录、隐藏文件、带特殊权限的节点。如果直接递归遍历然后逐个累加文件大小,速度慢不说,还容易中途报错。
我写了一个经过反复优化的函数:
function Get-DirectorySize { param([string]$Path) if (-not (Test-Path -Path $Path)) { return 0 } $size = 0 try { $size = (Get-ChildItem -Path $Path -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum } catch { Write-Warning "无法获取目录大小: $Path,原因: $($_.Exception.Message)" } return [math]::Round($size / 1MB, 2) }这里有两个细节值得注意。第一,-Force参数必须加,否则隐藏文件、系统文件不会被统计进去,很多临时文件恰恰是带隐藏属性的。第二,-ErrorAction SilentlyContinue也不能省,因为遍历过程中一定会遇到没有权限访问的子目录,如果让它报错,脚本直接中断,后面的清理全没了。
不过这个方法有一个性能问题:目录里文件越多,遍历一次越慢。实测下来,对一个文件数超过5万的目录,一次完整遍历可能要花十几秒。如果清理目标里有多个大目录,整个扫描阶段耗时可能会超过一分钟。这种情况我建议加一个进度提示,让用户知道脚本还在正常工作,不光是干等着。
4.3 第三步:实现安全的删除逻辑
删除逻辑是整个工具的核心,决定了它会不会误删、会不会中断。我这么写:
function Clear-TargetDirectory { param( [string]$Path, [int]$MaxAgeDays = 0 ) if (-not (Test-Path -Path $Path)) { Write-Host "跳过不存在的目录: $Path" -ForegroundColor Yellow return 0 } $beforeSize = Get-DirectorySize -Path $Path $items = Get-ChildItem -Path $Path -Force -ErrorAction SilentlyContinue $deleteCount = 0 $failCount = 0 foreach ($item in $items) { # 按时间策略过滤,只删除超过保留天数的文件 if ($MaxAgeDays -gt 0) { $age = (Get-Date) - $item.CreationTime if ($age.TotalDays -lt $MaxAgeDays) { continue } } try { Remove-Item -Path $item.FullName -Recurse -Force -ErrorAction Stop $deleteCount++ } catch { # 删除失败:多半是文件被占用或权限不足,记录后跳过 Write-Warning "无法删除: $($item.FullName),原因: $($_.Exception.Message)" $failCount++ } } $afterSize = Get-DirectorySize -Path $Path $released = [math]::Round(($beforeSize - $afterSize), 2) Write-Host "清理完成 [$Path],删除 $deleteCount 项,跳过 $failCount 项,释放约 ${released} MB" -ForegroundColor Green return $released }这段代码里有几个关键点:
删除前后各统计一次目录大小,差值就是真实释放的空间,这个方法比累加删除文件字节数可靠得多。因为你删除时可能遇到部分文件被占用删不掉,直接累加会高估释放空间。
Remove-Item后面必须带上-ErrorAction Stop,这样删除失败时会进入 catch 分支被记下来,而不是直接抛异常中断整个循环。
删除项可能是子目录。-Recurse -Force确保目录里的隐藏文件、子文件夹都能被一并清除。
4.4 第四步:回收站单独处理,不能走普通文件删除流程
回收站比较特殊,普通文件系统命令无法直接用它做遍历。我单独写了一个处理函数,利用 Windows Shell COM 接口来统计和清空:
function Clear-RecycleBinAuto { $shell = New-Object -ComObject Shell.Application $recycleBin = $shell.NameSpace(0xA) $beforeCount = @($recycleBin.Items()).Count $beforeSize = 0 @($recycleBin.Items()) | ForEach-Object { $beforeSize += $_.Size } if ($beforeCount -eq 0) { Write-Host "回收站已经是空的,跳过。" -ForegroundColor Gray return 0 } Write-Host "回收站当前有 $beforeCount 项,占用 $([math]::Round($beforeSize / 1MB, 2)) MB,开始清空..." Clear-RecycleBin -DriveLetter $env:SystemDrive -Force -ErrorAction SilentlyContinue Write-Host "回收站已清空。" -ForegroundColor Green return [math]::Round($beforeSize / 1MB, 2) }回收站路径对应的 Shell 命名空间 ID 是0xA。如果你希望清空前给用户一个检查机会,可以在Clear-RecycleBin之前暂停,输出提示让用户手动看一眼回收站有没有漏网的重要文件。我个人建议保留这个确认步骤,安全第一。
4.5 第五步:主流程串联,先扫描后确认再执行
辅助函数都准备好了,主流程只需要把它们串起来。
Write-Host "========== 开发环境临时文件清理工具 ==========" -ForegroundColor Cyan Write-Host "开始扫描,请稍候..." # 第一阶段:全量扫描,统计每个目标的占用空间 $scanResults = @() foreach ($target in $cleanupTargets) { $size = Get-DirectorySize -Path $target.Path $scanResults += [PSCustomObject]@{ 目录名称 = $target.Name 目标路径 = $target.Path 占用大小MB = $size } } # 第二阶段:展示扫描结果,等待用户确认 Write-Host "`n扫描结果如下:" -ForegroundColor Cyan $scanResults | Format-Table -AutoSize $totalWaste = ($scanResults | Measure-Object -Property 占用大小MB -Sum).Sum Write-Host "预计可释放空间: $([math]::Round($totalWaste, 2)) MB" -ForegroundColor Cyan $confirm = Read-Host "`n是否执行清理?(输入 N 或 n 取消,其他键继续)" if ($confirm -eq "N" -or $confirm -eq "n") { Write-Host "已取消清理,没有做任何修改。" -ForegroundColor Yellow exit } # 第三阶段:逐个执行清理 $releasedList = @() foreach ($target in $cleanupTargets) { $released = Clear-TargetDirectory -Path $target.Path -MaxAgeDays $target.MaxAgeDays $releasedList += [PSCustomObject]@{ 目录名称 = $target.Name 释放空间MB = $released } } # 回收站单独处理 $recycleReleased = Clear-RecycleBinAuto if ($recycleReleased -gt 0) { $releasedList += [PSCustomObject]@{ 目录名称 = "回收站" 释放空间MB = $recycleReleased } } # 第四阶段:汇总报告 Write-Host "`n========== 清理完成报告 ==========" -ForegroundColor Green $releasedList | Format-Table -AutoSize $finalReleased = ($releasedList | Measure-Object -Property 释放空间MB -Sum).Sum Write-Host "总共释放空间: $([math]::Round($finalReleased, 2)) MB" -ForegroundColor Green Write-Host "重要数据不受影响,清理过程已结束。" -ForegroundColor Cyan整个主流程的设计核心是"先给人看,再让人决定"。第一阶段的扫描只统计不删除,所以哪怕你在开会时手滑双击运行了脚本,唯一的后果就是屏幕上多了一张统计表,不会造成任何实际修改。
5. 让工具真正"自动化":日志、白名单与定时执行
一个工具要真正好用,光能跑还不行。你需要给它加上日志、白名单、定时任务,这样它才能真正省心。
5.1 给工具加上运行日志,出问题能回溯
清理工具不同于普通脚本,一旦运行就会接触大量文件。如果清理后系统出问题,你第一个想知道的必然是"刚才到底删了什么"。所以我强烈建议加上日志功能。最简单的方式是利用Start-Transcript:
$logDir = "$env:USERPROFILE\Documents\CleanupTool\Logs" New-Item -ItemType Directory -Force -Path $logDir | Out-Null $logFile = "$logDir\cleanup_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" Start-Transcript -Path $logFile -Append | Out-Null # ... 脚本主体 ... Stop-Transcript | Out-Null Write-Host "日志已保存到: $logFile"Start-Transcript会将控制台所有输出同时写入日志文件,包括Write-Host、错误信息、警告。这个方法零成本,也不需要额外引用模块。如果将来需要更精细的日志,可以改用自定义函数往 CSV 里登记每一次删除的文件路径。
5.2 加入白名单,防止误删重要数据
清理工具最大的风险永远是误删。哪怕你已经设置了按时间过滤,也保不齐某些软件会把重要配置文件写到临时目录里。我的做法是引入一个白名单关键词数组:
$excludePatterns = @("*重要*", "*备份*", "*backup*", "*TempDB*", "*autosave*") function Test-ExcludedPath { param([string]$Path) foreach ($pattern in $excludePatterns) { if ($Path -like $pattern) { return $true } } return $false }然后在Clear-TargetDirectory函数里,删除之前先调用判断:
if (Test-ExcludedPath -Path $item.FullName) { Write-Host "跳过白名单路径: $($item.FullName)" -ForegroundColor Yellow continue }这样即便某天你的清理规则写得太激进,只要路径名匹配了白名单关键词,文件就能安全被跳过。还有一个小技巧:把个人文档目录、照片目录、软件安装目录全部加入到"永不清理"的硬编码列表里,防止未来扩展清理规则时不小心把它们卷进去。
5.3 挂上Windows任务计划程序,实现定时自动清理
脚本写完之后,下一步就是让它每周定时跑一次。Windows 内置的任务计划程序完全够用,不需要额外装东西。
推荐的任务配置:
- 打开"任务计划程序",选择"创建基本任务"。
- 名称填写"开发环境临时文件清理",触发器选择"每周",比如每周五晚上 18:00 运行。这个时间点适合一周工作收尾的时候。
- 操作选择"启动程序",程序填
powershell.exe,参数填-ExecutionPolicy Bypass -File "D:\Scripts\cleanup.ps1"。 - 如果希望它静默运行不需要人盯着,就加上
-WindowStyle Hidden。但请注意,隐藏窗口之后如果出问题你也不容易发现,所以建议搭配前面的日志一起使用。
如果你想要完全免确认运行,可以给脚本加一个-AutoClean参数:
param([switch]$AutoClean) if ($AutoClean) { $confirm = "Y" } else { $confirm = Read-Host "是否执行清理?(N/n 取消)" }这样一来,手动运行时仍会给你确认机会,定时任务运行时则传-AutoClean直接执行清理。
6. 常见问题与排查技巧实录
再完美的脚本,放到真实环境里也会遇到各种幺蛾子。这部分是我实际跑这个工具时踩过的坑,一个个列出来,方便大家少走弯路。
6.1 "文件正在使用"无法删除,怎么办
最常见的失败原因就是目标文件被某个进程占用了。Chrome 开着的时候去删它的缓存、IDE 在后台编译的时候去删它的临时目录,都会碰到这种情况。
处理办法有三个层级:
- 脚本里捕获异常,记录之后跳过,不中断整体流程。我的
Clear-TargetDirectory函数就是这么处理的。 - 如果被占用文件占比很高,先提示用户关闭浏览器、IDE、编译工具等大型软件,再重新运行清理。
- 如果确实要清理一个正在运行服务所占用的文件,比如某个后台服务的日志文件,需要先停止该服务,清理完成后再启动。这属于高级操作,不建议放在通用脚本里,除非你有明确的需求。
6.2 "拒绝访问"权限不足导致清理失败
清理C:\Windows\Temp或者回收站这类系统级目录时,如果当前 PowerShell 没有以管理员身份运行,删除时就会出现"拒绝访问"。
我的解决办法是在脚本开头加一个权限校验和自动提权逻辑:
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()) .IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { Write-Host "当前不是管理员身份,尝试自动提权..." -ForegroundColor Yellow Start-Process powershell.exe -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs exit }这段代码的作用是:检测到不是管理员权限时,自动用 UAC 弹窗请求管理员权限,然后以管理员身份重新运行当前脚本。
注意:如果脚本放在网络路径或者有特殊权限限制的目录,自动提权可能会失败。这种情况下请手动右键选择"以管理员身份运行"。
6.3 清理后某个软件启动异常,怎么办
这个现象多半是因为删除了软件运行期生成的缓存文件,比如动态编译缓存、预生成程序集等。解决办法是:
- 先看日志,确认清理了哪些路径下的文件。
- 重启出问题的软件,绝大多数软件会自动重建所需缓存,只是首次启动会慢一些。
- 如果重启后问题依旧,尝试重装该软件。
- 最后,如果重装都不能解决,检查是否误删了软件配置目录下的非缓存文件。出现这种问题,大概率是你在
cleanupTargets里加了过激的清理规则。
避免这个问题最有效的手段就是:正式清理前先关掉所有大型软件,让脚本在尽可能少的进程干扰下运行。
6.4 清理完磁盘空间没怎么变,什么情况
出现这种情况,先别急着怀疑脚本没效果。有四个可能:
- 脚本可能因为权限不足,大量文件删除失败,只跳过了部分。
- 如果你开启系统休眠且启用了休眠文件,
C:\hiberfil.sys会占用几个G到十几个G,这不在临时文件清理范围内。 - 虚拟内存文件
pagefile.sys也是同一种情况。 - 如果清理的是大量小文件,磁盘上释放出来的"可用空间"并不会立刻反映在资源管理器的百分比上,有时候需要刷新一下。
想验证脚本是否真的工作了,最直接的方式是看输出报告里的汇总数字,以及日志文件里的删除记录。
6.5 路径包含中文空格等特殊字符
bat 对空格和中文路径支持很差,但 PowerShell 基本没有这个问题。不过有一个细节容易忽略:如果在 PowerShell 中调用外部 exe 程序,路径参数依然要自己加引号,否则对方接到的参数会被截断。
比如你想在清理完成后调用cleanmgr.exe来进一步清理系统文件,就需要写成:
$arguments = "/d $env:SystemDrive /sagerun:1" Start-Process -FilePath "cleanmgr.exe" -ArgumentList $arguments -Wait7. 进阶扩展:把清理范围扩大到完整开发链路
基础版工具跑通之后,我发现清理范围还能进一步扩大。别忘了,开发者电脑上最占空间的往往不是系统临时文件,而是各种依赖缓存和构建产物。
7.1 加入语言包管理器的缓存清理
开发不同的技术栈,会产生不同的缓存目录。下面这段配置可以按需添加:
@{ Name = "Maven仓库"; Path = "$env:USERPROFILE\.m2\repository"; MaxAgeDays = 30 }, @{ Name = "Gradle缓存"; Path = "$env:USERPROFILE\.gradle\caches"; MaxAgeDays = 30 }, @{ Name = "Go模块缓存"; Path = "$env:USERPROFILE\go\pkg\mod"; MaxAgeDays = 60 }, @{ Name = "NuGet缓存"; Path = "$env:USERPROFILE\.nuget\packages"; MaxAgeDays = 60 }, @{ Name = "pnpm缓存"; Path = "$env:LOCALAPPDATA\pnpm-cache"; MaxAgeDays = 14 }, @{ Name = "Yarn缓存"; Path = "$env:LOCALAPPDATA\Yarn\Cache"; MaxAgeDays = 14 }这些目录删除策略要比系统临时文件更保守。MaxAgeDays建议设置成30到60天,而不是7天。因为开发工具重新下载依赖包的代价非常大,只删长期不用的缓存是最合适的选择。
7.2 清理项目构建产物目录
如果你的磁盘上有大量旧项目存档,里面通常残留着build、dist、target、node_modules等目录。这部分体积往往比临时缓存更惊人。
我加了一个可选功能:扫描指定项目根目录下的子项目,找出超过90天未修改的构建产物目录并清理。注意这个功能有风险,必须让用户逐个确认,绝不能写进自动化流程里。实际使用时,我一般会在一次大扫除中手动运行这个逻辑。
7.3 清理完成后发送通知
定时任务跑到深夜,第二天早上怎么知道清理结果?我给脚本加了几行代码,用 PowerShell 内置的Send-MailMessage把结果发到邮箱:
if ($finalReleased -gt 0) { $mailParams = @{ From = "cleanup@example.com" To = "me@example.com" Subject = "开发环境清理报告 - $(Get-Date -Format 'yyyy-MM-dd')" Body = "本次清理共释放 $finalReleased MB 空间。" SmtpServer = "smtp.example.com" Port = 587 UseSsl = $true Credential = (Get-Credential) } Send-MailMessage @mailParams }如果不方便配置邮箱,也可以用 Windows 通知中心弹提示,或者直接把结果追加到一个固定的报告文件里。核心思路是让结果可追踪、可审计。
8. 实战数据分享:清理效果到底怎么样
最后分享一次我在这台日常开发机上实际运行的数据,给大家一个体感参考。
我平时主力机是 Win11,常开的应用包括 VS Code、Chrome(十几个标签页)、Docker Desktop、微信、飞书,偶尔开 IntelliJ IDEA。一个月左右不清理,第一次全量扫描的结果大概是:
- 用户临时目录
%TEMP%:2.1 GB - 系统临时目录
C:\Windows\Temp:420 MB - Windows 更新缓存:3.8 GB
- IE/Edge 缓存:180 MB
- Chrome 缓存:650 MB
- npm 缓存:1.5 GB
- pip 缓存:900 MB
七项加起来,预计可释放约 9.5 GB。实际跑完之后,报告显示总共释放了 8.9 GB,其余约 600 MB 因为文件被占用或者权限不足没删掉,但整体效果已经非常可观了。整个流程从扫描到清理完成,大约花了四十秒,其中扫描阶段占用了一大半时间。
如果把 Maven 仓库、Gradle 缓存、Go 模块缓存也加进来,第一次扫的时候总共能释放出 20 多 GB。而且清理这些开发缓存之后,日常构建速度并没有明显变慢,因为常用的依赖包还是在的,只有那些几个月没碰过的旧项目才需要重新下载依赖。
说实话,自己动手写这套工具最大的收获不是那几十 G 磁盘空间,而是对 Windows 文件系统有了更细致的理解。比如哪些目录是系统核心,哪些目录是标准缓存,哪些文件删了还能自动重建,哪些文件删了就出大事。这些认知,用第三方清理工具是永远学不到的。
如果你也经常被磁盘空间困扰,不妨花一个下午照着这个思路自己写一个清理脚本,再按自己的软件使用习惯去调整清理规则。用自己亲手写的工具,看着它输出的那份清理报告,那种踏实感是任何花里胡哨的清理软件都给不了的。