帮同事A清一台老办公电脑的时候,我盯着系统盘剩余空间看了半天:回收站早就倒空了,桌面也干净,C盘却被塞得只剩几个G。逐个翻临时目录、缓存、日志,删到一半我就在想,这种事情为什么不写成一个脚本一次性解决?于是就有了这套纯bat脚本,迭代到现在的v3.6.2。这篇东西不是把我的代码贴出来就完事,我把整个设计逻辑、清理边界、权限处理、实测踩坑的过程都记录下来,希望能给同样想自己动手写清理脚本的人少走点弯路。
1. 为什么坚持用纯bat写清理工具
1.1 第三方清理工具的信任成本太高
前几年我也用过那些一键清理、深度清理的第三方工具,体验并不好。装一个清理软件,开机弹窗、安全播报、下载管家全家桶接踵而至,你想清理几个G的垃圾,它倒先往你硬盘里塞了几十个MB的安装包。更麻烦的是,很多清理工具的“深度优化”是黑盒操作,你不知道它到底删了什么,也没办法审计。出了问题别说恢复,连根因都找不到。后来我彻底放弃了这些工具,选择自己动手。
自己写清理脚本,首先解决的是信任问题。bat脚本是纯文本,每一行命令都是白纸黑字,删什么、往哪删、有没有排除条件,全部摊开在眼前,我可以逐行检查,出了任何问题也能快速定位。这对处理同事和朋友的电脑尤其重要,至少出了状况不会被人质疑“你是不是装了什么看不懂的东西”。
1.2 系统自带能力其实足够支撑日常清理
系统盘的内存杀手,翻来覆去就是那么几类:临时文件、缓存、日志、更新残留、回收站。这些都可以用命令删掉。系统自带了一个磁盘清理工具,但默认设置比较保守,很多项目不会自动勾选,而且它的执行效率不算高。更重要的是,它没法做成可批量重复、可预设定时执行的方式。
纯bat的优势在于它可以完全按需定制。我可以把清理项分成“安全级”和“可选级”,安全级默认执行,可选级用参数启动。这样既能一键处理绝大多数垃圾,又能在特殊场景下做深度清理。整个脚本能放进U盘,拷贝到任何一台电脑上双击运行,不需要装任何运行时环境,这在实际维护中特别方便。
1.3 为什么不用PowerShell或者VBS
PowerShell功能确实比bat强太多,但有两个问题让我最终选择了bat作为主载体。一是PowerShell脚本的执行策略在默认环境下是受限的,换一台电脑跑还得先解除策略,如果在别人家里去调这个,用户会非常不安。二是bat脚本的“可见性”更高,用记事本打开就能读懂,非技术用户也知道怎么把关键内容截给你看,排查成本低。VBS则更冷门,兼容性也不让人省心。
所以说,纯bat并不是因为它技术含量高,而是因为它足够简单、足够透明、足够“笨”。但这个“笨”恰恰是我要的东西。
2. v3.6.2到底清理什么:清理清单与边界设计
2.1 默认清理项目与路径
v3.6.2的第一版设计思路很简单:把我手动清理时经常访问的目录全部列出来。经过几轮调整,默认清理项基本固定如下。
| 清理对象 | 默认路径或处理方式 | 风险等级 | 说明 |
|---|---|---|---|
| 用户临时文件 | %TEMP% | 低 | 各类软件运行时产生的临时文件 |
| 系统临时目录 | C:\Windows\Temp | 低 | 系统组件和更新过程留下的临时内容 |
| 回收站 | 各盘符下的$Recycle.bin | 中 | 删除前有确认提示,默认保留 |
| 缩略图缓存 | %LOCALAPPDATA%下的Explorer缓存目录 | 低 | 重建速度很快,不影响系统运行 |
| 更新下载缓存 | C:\Windows\SoftwareDistribution\Download | 中 | 需要先停止更新服务,处理完再启动 |
| 错误报告与内存转储 | %LOCALAPPDATA%\CrashDumps等 | 低 | 对普通用户意义不大 |
| 日志文件 | C:\Windows\Logs下部分子目录 | 中 | 只清理超过30天的旧日志 |
| DNS解析缓存 | ipconfig /flushdns | 极低 | 释放后会自动重建 |
| 预读取缓存 | C:\Windows\Prefetch | 可选 | v3.6.2默认不清理,原因后面详述 |
每个清理项都用“先探测目录是否存在,再执行删除”的方式处理。在默认清理模式下,我要求所有项目都必须能用一句话解释清楚“删掉之后系统会自动重建什么”。如果一项内容删掉后不能自动恢复,就绝不放进默认清理列表,必须改成可选清理或者直接不清理。
2.2 需要明确的“不清理”边界
写清理脚本最危险的地方不是没删干净,而是删过头。我在v3.6.2里设置了一个硬性排除列表,任何情况下都不会去碰这些目录:C:\Windows\System32、C:\Windows\WinSxS、C:\Program Files、C:\Program Files (x86)、系统还原点目录、用户Profile目录下的文档和桌面。
有些人可能不理解为什么要排除WinSxS。这个目录叫组件存储,里面存放着系统更新用的旧版本组件,用磁盘清理工具清理“Windows更新清理”是可以释放一部分空间的,但如果用命令强行删里面具体文件,系统可能直接无法启动或后续更新全挂。避坑的唯一原则就是:不确定的就不要动。
排除列表在脚本里是一个变量组,后续加白名单非常方便。毕竟v3.6.2已经迭代了好几个版本,每次新增清理项之前,我都会先问一个问题:如果这个目录里的文件全部删除,系统或常用软件会不会出现不可逆的异常?答案是不确定,那就直接放弃,不写进脚本。
2.3 回收站处理策略的演进
回收站是一个争议点。清空回收站确实能释放不少空间,但误删文件的人往往会把最后希望寄托在回收站上。v3.6.2的处理方式是:默认模式不清空回收站,只是提示还剩余多少空间可以释放。只有在显式加大写参数“-deep”时,才会执行各盘符$Recycle.bin目录的清理,而且执行前会再弹一次确认框。
有段时间我图省事,默认就把回收站包含进去,结果真实翻过一次车。同事A误删了一份合同文档,刚好那批文档还没有备份,回收站又被我上一轮清理脚本顺手倒空了。那次之后我把回收站从默认清理项中挪到了深度清理分支,宁可少清理一点,也不给人添麻烦。这个教训之后,我再也没有收到过关于误删文件的投诉。
3. 核心代码逻辑:从单条命令到流程编排
3.1 提权与自启动逻辑
bat脚本清理系统临时目录和更新缓存时,必须拥有管理员权限。普通双击是没有权限的,硬删会得到一堆“拒绝访问”的提示。所以脚本启动时,第一件事就是检测权限,权限不足则利用系统自带的PowerShell以管理员身份重新启动自身。
@echo off setlocal enabledelayedexpansion title 系统垃圾清理工具 v3.6.2 >nul 2>&1 net session if %errorlevel% neq 0 ( powershell -Command "Start-Process -FilePath '%~f0' -ArgumentList 'admin' -Verb RunAs" exit /b ) set "LOG=%~dp0CleanLog_%date:~0,4%%date:~5,2%%date:~8,2%.log" echo ============================================ >> "%LOG%" echo [%date% %time%] 清理任务启动,运行模式:%1 >> "%LOG%" call :ClearDefault call :ClearUpdateCache call :FlushDns echo [%date% %time%] 默认清理完成 >> "%LOG%" if /i "%~1"=="-deep" call :ClearRecycle if /i "%~1"=="-deep" call :ClearPrefetch echo 清理完成,详情见日志文件 pause exit /b这段代码有几个值得注意的点。首先,用net session检测管理员权限,效果直观,多数环境下不会有副作用。其次,日志文件名按日期生成,方便日后倒查某一天到底清理了什么。第三,%~1参数只接受“-deep”,其他任何内容都按默认模式处理,避免误触发深度清理。
3.2 临时文件清理函数的设计
默认清理的核心函数是删除临时目录下的文件与子目录。这里有个常见的错误:直接用del /f /s /q %TEMP%删掉整个目录,虽然也能清完,但会把临时目录的权限和属性搞乱。正确处理是先删除文件,再逐层删除顶层子目录。
:ClearTemp for %%D in ("%TEMP%" "%SystemRoot%\Temp") do ( if exist %%D ( echo [%date% %time%] 清理临时目录:%%D >> "%LOG%" del /f /s /q "%%D\*" >nul 2>&1 for /d %%P in ("%%D\*") do ( rd /s /q "%%P" >nul 2>&1 ) ) ) goto:eofdel命令负责删除文件,for /d配合rd负责删除子目录。有些文件正被占用,删除会失败,这没关系,我们重定向错误输出后就跳过,不影响其他内容的清理。真正需要关注的是,如果某个子目录删除失败,可能有程序正在读写,不要强试。强试的方式是在此基础上不断重试,但bat没有天然的后台等待机制,做起来很僵硬,反而可能拖慢整体速度。
3.3 旧日志的按时间筛选方法
日志文件不能直接一股脑全部删。比如C:\Windows\Logs下有些子目录的体积虽然不小,但里面可能包含系统服务排障需要的历史记录。v3.6.2默认只清理30天之前的日志,这个逻辑用系统自带的forfiles命令实现。
forfiles /p "C:\Windows\Logs" /s /m *.log /d -30 /c "cmd /c del /f /q @path" >nul 2>&1需要注意,forfiles在处理大量文件时效率一般,而且对路径含空格的情况必须使用双引号包裹。实测下来,一台积累了两三年日志的机器,首次执行forfiles大约需要几十秒,之后每次都在几秒内结束。如果你需要清理的是大量小文件,可以退而求其次,单纯用del加目录排除,效率更好,但时间筛选就没这么方便了。
3.4 更新缓存的特殊处理
Windows更新下载的安装包缓存在C:\Windows\SoftwareDistribution\Download。直接删除会有部分文件被占用,因为更新服务和后台传输服务正在工作。因此清理前必须停止相关服务,删除完成后再重启服务。
:ClearUpdateCache echo [%date% %time%] 开始清理更新缓存 >> "%LOG%" net stop wuauserv >nul 2>&1 net stop bits >nul 2>&1 if exist "%SystemRoot%\SoftwareDistribution\Download\*" ( del /f /s /q "%SystemRoot%\SoftwareDistribution\Download\*" >nul 2>&1 ) net start wuauserv >nul 2>&1 net start bits >nul 2>&1 goto:eof网上有些脚本会把SoftwareDistribution整个目录删掉,我强烈建议不要这样做。这个目录里面除了Download以外,还有数据存储等其他内容,整体删除后系统会在后台重新构建,但中途可能造成更新状态错乱,让你的系统在“检查更新”界面卡很久。只动Download子目录,是风险与收益最平衡的方案。
4. 安全机制:权限、日志、恢复三件套
4.1 排除列表与内容白名单
很多人写清理脚本只考虑“能删多少”,不考虑“哪些绝不能碰”。我在脚本里专门设置了一个SKIP_LIST变量区,把需要保护的路径集合放在最前面,所有删除函数都会先检查目标是否命中排除列表。
讲一个实现细节:bat脚本没有数组,所以排除列表我用的是带分隔符的字符串,通过findstr配合管道来进行匹配。例如:
set "SKIP_LIST=C:\Windows\System32; C:\Windows\WinSxS; C:\Program Files; C:\Program Files (x86)"对应的是:当某个清理项路径在排除列表中时,直接跳过并写入日志。这样可以防止后续加新功能时,某个清理逻辑不小心扫到系统核心目录。退出脚本前,会把本次执行过程中所有跳过的项都打印出来,让你知道哪些内容被保护了。
4.2 日志审计与差异对比
日志文件是我在v3.0版本后开始强制加入的。早期版本里,脚本执行完就完了,出了异常根本不知道是哪一步导致的。后来加上了比较完整的日志体系,每一类清理操作都记录时间、路径、执行状态。日志的另一个好处是方便前后对比:这次清理前空间多少,清理后空间多少,下一次再扫的时候就能评估垃圾增长速度。
在v3.6.2里,日志采用追加模式,不会覆盖历史记录。日志文件按天命名,放在脚本所在目录。实际使用中如果怀疑清理有问题,把日志发给我,我一眼就能看出它执行了哪些分支,有没有跳过什么,比用户描述“反正就是变慢了”要高效得多。
4.3 建议数据备份与可回滚策略
纯bat脚本本身很难实现完美的“回收站”机制,因为文件删除后无法找回。v3.6.2采取了一个折中策略:凡是风险等级为“中”的项目,在执行删除之前,都会尝试把待删除文件列表写入一个备份清单文件。虽然这不能恢复数据,但至少能让人知道删了哪些具体文件、从哪些路径删除的,万一某软件依赖其中某个文件,排查方向就很明确。
更保险的做法是,在给别人清理电脑之前,先手动做一次重要资料的备份。现在显卡和硬盘都比较普遍,移动硬盘也不太贵。个人数据永远是第一位的,脚本清理的是系统垃圾,不是你的文件。在给不太懂的亲戚朋友使用脚本前,我都会在说明里加一句:重要文件请先备份到别的盘,再运行本脚本。
4.4 深度清理的要谨慎
深度清理分支只在显式传入-deep参数时触发,除了清空回收站外,还会处理缩略图缓存、错误报告文件夹。这些项目虽然安全,但会丢失一些个性化状态,比如缩略图全部重建时,资源管理器初次打开会慢一点。所以深度清理默认不执行,也不建议普通用户频繁执行。
实际上,大多数人的电脑垃圾增长,都是临时文件和缓存导致的,默认模式已经能解决80%以上的空间问题。深度清理更适合在空间极度紧张或者需要重装系统前过渡一下。这个判断是我在几次“清理后系统变慢”的反馈中总结出来的,高频率深度清理对用户体验并不友好。
5. 实测中的一次严重翻车:预读取缓存引发的连环问题
5.1 问题现象与初步诊断
v3.5版本之前,我曾经把C:\Windows\Prefetch目录加入过默认清理。测试的时候清理效果很好,能释放出不少小文件,系统重启之后看起来也正常。后来有一个同事反馈,说运行完清理脚本后,某款办公软件启动变慢,而且那台电脑的网络共享访问出现了权限提示。
我一开始以为是偶发问题,没太在意。但后来有第二个人反映类似现象,我才意识到可能是清理脚本动了不该动的东西。排查的第一步就是看日志,日志显示当天的清理流程确实执行了ClearPrefetch分支。
5.2 完整的排查链路
我先用排除法恢复验证。把那台机器的清理脚本改回不清理Prefetch,重新跑一遍,重启之后再让同事打开同一个办公软件,问题消失了。到这里基本可以确定,Prefetch被清理后,确实影响了部分软件的行为。
为什么会有这种影响?Prefetch的机制是为了加速常用程序启动,把程序启动时读取的文件信息缓存起来。某些软件在安装时会往启动流程里写入动态生成的临时文件,启动路径存放在Prefetch中或者依赖Prefetch的效果。一旦缓存被大规模删除,软件在首次冷启动时逻辑上出现变化,极个别带许可校验的软件甚至会误判为“环境变化”,进而触发二次激活流程,表现为异常弹窗。
这不是说Prefetch本身绝对不能清,而是说它属于“有可能影响软件行为”的项,普通用户很难判断什么情况下清理收益大于风险。我最终把它从默认清理中移除,只在深度模式下挂载,同时在使用说明里明确标注“清理预读取缓存可能导致常用软件首次启动变慢”。
5.3 修复方案与回归测试
这次的教训让我意识到,脚本功能再小也要做回归测试。修复方案分两步:一是代码层面把Prefetch从默认清理函数中分离出来,挪进-deep模式;二是给每个清理分支增加了独立的开关注释,方便后续维护。在重新发版前,我在三台不同系统上做了循环测试,分别跑默认模式和深度模式,重点观察软件启动速度、系统服务状态和网络共享访问,确认没有异常才最终定为v3.6.x分支。
从那以后,“不加不确定项”就成了这个脚本的铁律。宁可功能保守,也不制造新的麻烦。
6. 兼容性优化与维护节奏
6.1 不同系统版本的真实差异
v3.6.2主要在较新的系统环境下测试,但我同样在旧设备上跑过,差异还是比较明显的。命令行工具基本一致,但权限管理不同:新系统对系统目录的写入保护更严格,删除时经常需要借助takeown和icacls摄取所有权,而在老系统上直接删除往往就能成功。
我在清理系统临时目录时加入了一小段处理逻辑:删除前先尝试直接del,失败再以静默方式执行takeown /f和icacls /grant,这样既能兼容老系统,又不拖累新系统的清理速度。用net session判断管理员权限的方式也通用,不依赖新版本特有命令。
6.2 定时清理与无人值守模式
手动双击脚本当然可以,但养成习惯很难。我更推荐配合系统自带的任务计划程序,实现每周自动清理一次。在bat脚本里直接贴一行schtasks命令就可以注册任务:
schtasks /create /tn "SystemCleanup" /tr "C:\Path\To\Cleanup.bat admin" /sc weekly /d sun /st 03:00 /ru SYSTEM /f需要注意,任务计划里用/ru SYSTEM运行时,路径和权限都不一样,脚本内部必须确保日志目录可写,否则会直接失败。我把日志路径改成了指向脚本所在目录,然后用/ru指定当前用户比较稳妥。定时任务虽然方便清理,但深度清理不建议定时,万一碰到有重要文件在回收站里,定时清空会出问题。
6.3 脚本体积与执行效率之间的平衡
bat脚本效率不高,无论是文件扫描还是循环删除,在文件数量很多的时候都会明显变慢。我的优化思路是控制范围,不做全盘扫面,只扫描已知路径。把几百个G的全盘搜索拆掉,换成十几个固定目录的遍历,速度会快上不少。
同时,在删除大量小文件时,先跑del /f /s /q再跑rd /s /q,比逐层递归删除要快。遇到过执行时间过长的情况,我会在脚本的for循环里判断系统负载,当资源占用高的时候提前退出清理,把剩余任务留给下次。考虑到后台可能还有其他程序在跑,这种“适度礼让”是有必要的。
6.4 个人使用与给别人使用时的心态差异
写脚本给自己用,可以大胆一点,反正出了问题我有能力自己排查。但给别人用就不一样了,对方不一定有备份习惯,也不一定了解脚本的原理,任何一次误删都可能造成信任崩塌。v3.6.2在对外提供时,默认模式极度保守,深度模式我从来不会不开说明就直接丢给别人执行。
我也在脚本的echo提示里增加了大量中文说明,执行到每一步都会告诉用户“正在做什么”“为什么做”。这种可见性设计让使用者在运行过程中不那么心慌,出了状况也能清楚地向技术人员转述。对我自己来说,每一条命令都经得起追问,是最基本的要求。
关于这个工具的后续,我其实没打算加入太多新花样。真正的中年实用主义者都明白,清理工具最重要的不是功能强大,而是稳定、可预期、不惹祸。v3.6.2目前的状态,就是能满足绝大多数日常需求、又不会制造新问题的版本。如果你也打算自己写清理脚本,我的建议很直接:从最小清单开始,逐条加白名单,永远不要试图一版到位。先保证运行一百次不翻车,再考虑能不能多清出两个G。