
1. 为什么我至今还在用 bat 做系统清理很多人一听到“清理系统垃圾”第一反应是装个某某管家、某某优化大师或者去网上找那种一键清理的绿色小工具。我早些年也这么干过后来发现两个问题一是这些工具本身越装越臃肿后台常驻进程、弹窗、捆绑安装防不胜防二是它们清理的逻辑是个黑盒你根本不知道它删了什么、留了什么偶尔还会误删导致某个软件打不开。折腾久了我反而回到了最原始的路子——自己写一个 Windows 批处理脚本也就是大家常说的 bat 文件。Windows bat 清理系统垃圾文件这件事说白了就是用系统自带的命令解释器 cmd把那些长期堆积、可以安全删除的临时文件、缓存、日志、回收站内容批量清掉。它最大的价值在于零依赖、零安装、完全透明、可自己掌控删什么不删什么。你不需要装任何第三方软件Windows 自带就能跑脚本内容全是纯文本打开就能看懂每一行在干什么想改规则改一行字就行。这篇文章适合谁看如果你是会一点点电脑操作、想给自己或家里老人的电脑做个定期清理的普通用户那完全够用如果你是运维或者开发想给一批机器做批量维护这套思路同样能扩展。我会从设计思路讲到每一行命令的写法再把我这些年踩过的坑、误删过的教训、以及怎么让脚本更安全全都摊开讲清楚。你照着抄改改路径就能用。2. 脚本整体设计与清理范围拆解2.1 先想清楚哪些能删哪些碰不得写清理脚本第一件事不是敲代码而是划边界。系统里能删的东西和不能删的东西界限其实很清晰但新手最容易在这里翻车。可以安全清理的通常是这几类用户临时目录%TEMP%和%TMP%软件安装、运行时产生的临时文件重启后大部分本来就没用了。系统临时目录C:\Windows\Temp系统级临时文件注意这个目录普通权限可能删不干净需要管理员权限。浏览器缓存Chrome、Edge 等的 Cache 目录删了只是下次访问网站慢一点不影响收藏和密码。回收站这个不用多说但要注意别把用户还没确认要删的东西一起清了。系统日志与转储比如C:\Windows\Logs、内存转储文件MEMORY.DMP这些动辄几个 G。预读取文件C:\Windows\Prefetch这个有争议后面单独说。Windows 更新缓存C:\Windows\SoftwareDistribution\Download更新装完后这些包就没用了但删之前最好确认没有正在进行的更新。绝对不能碰的C:\Windows\System32、C:\Windows\SysWOW64、用户文档目录、注册表文件、pagefile.sys、hiberfil.sys休眠文件要用专门命令关不能直接删。这些一旦误删轻则软件报错重则系统起不来。我个人的原则是脚本只删“明确知道用途、且删了不影响功能”的目录凡是拿不准的一律不碰。宁可少清一点也不要为了多腾几个 G 去冒险。2.2 为什么用 bat 而不是 PowerShell 或 Python有人会问现在 PowerShell 功能强多了Python 也方便为什么还用老掉牙的 bat我的理由很实在第一兼容性。bat 从 Windows 98 时代活到现在XP、Win7、Win10、Win11 全都能跑你给一台十年前的老机器发个脚本双击就能用。PowerShell 有执行策略限制默认可能不让跑脚本还得先改策略对普通用户不友好。第二权限门槛低。bat 双击即运行需要管理员权限时右键“以管理员身份运行”就行不需要额外配置环境。第三可读性对普通人友好。del、rd、copy这些命令稍微懂点电脑的人都能看懂改起来没心理负担。当然 bat 也有短板比如处理中文路径容易乱码、错误处理能力弱、没有原生的数组和函数。但这些在清理场景里都能绕过去后面我会讲怎么处理中文和错误。2.3 脚本的整体结构设计一个靠谱的清理脚本我一般分成四块环境准备设置编码、请求管理员权限、定义变量比如要清理的路径列表。安全检查确认关键路径存在、确认用户有权限、给出确认提示。执行清理按类别逐个清理每步输出结果。结果汇总统计清理了多少、释放了多少空间、有没有失败项。这种分块的好处是出问题时你能快速定位是哪一块的毛病。比如清理没生效先看是不是权限没拿到中文路径报错先看编码设置对不对。提示脚本开头一定要加echo off否则每条命令都会回显屏幕刷得你眼花。调试阶段可以临时去掉它方便看执行过程。3. 核心命令与关键细节逐个拆解3.1 请求管理员权限的标准写法清理系统目录必须要管理员权限否则C:\Windows\Temp这类目录根本删不动。bat 自己提权有点绕标准做法是用一段自检代码检测当前是否管理员不是的话就用 PowerShell 重新拉起自己。echo off :: 检查管理员权限 net session nul 21 if %errorlevel% neq 0 ( echo 正在请求管理员权限... powershell -Command Start-Process %~f0 -Verb RunAs exit /b )这段逻辑的原理是net session这个命令只有管理员才能成功执行普通用户会返回错误码。用%errorlevel%判断不等于 0 就说明没权限于是调用 PowerShell 的Start-Process带上RunAs参数重新启动自己。%~f0是当前脚本的完整路径exit /b是退出当前这个没权限的实例避免重复执行。这里有个坑如果路径里有空格或中文Start-Process的参数要处理好引号否则会启动失败。我一般把脚本放在纯英文路径下省得折腾。3.2 编码问题中文乱码的根治办法bat 默认用系统 ANSI 编码简体中文系统是 GBK但如果你用记事本存成 UTF-8脚本里的中文就会变乱码甚至导致命令解析出错。解决办法有两个一是存文件时选 ANSI 编码这是最省事的。二是在脚本开头切换代码页chcp 65001 nulchcp 65001把控制台切到 UTF-8配合 UTF-8 保存的脚本中文就能正常显示。但要注意切了 65001 之后某些老命令对中文路径的处理会变怪所以我更推荐第一种——直接存 ANSI。我自己的习惯是脚本里尽量少写中文路径用变量代替输出提示用简短英文或拼音从根上避开编码问题。3.3 删除命令的选择del、rd、forfiles 怎么用清理文件主要靠三个命令各有各的适用场景del删文件。del /f /q /s 路径\*.*里/f强制删只读文件/q安静模式不询问/s递归子目录。注意del删不掉目录本身。rd或rmdir删目录。rd /s /q 路径里/s连同子目录一起删/q不询问。这个命令威力大路径写错就麻烦了。forfiles按时间筛选删除比如只删 7 天前的文件。forfiles /p 路径 /s /m *.* /d -7 /c cmd /c del path这个在清理日志时特别有用避免把刚生成的、还在用的文件删掉。我一般组合使用临时目录用del清文件、rd清空子目录日志目录用forfiles按天数删更稳妥。注意rd /s /q是不可恢复的删之前一定确认路径变量拼对了。我踩过一次坑变量没赋值成功结果rd /s /q 差点把当前目录清了幸好加了保护判断。3.4 路径变量的定义与保护把要清理的路径定义成变量既方便修改也方便加保护。比如set TEMP_DIR%TEMP% set SYS_TEMPC:\Windows\Temp set PREFETCHC:\Windows\Prefetch然后在删除前加一道判断防止变量为空导致误删if not defined TEMP_DIR ( echo 临时目录变量未定义跳过 goto :skip_temp ) if not exist %TEMP_DIR% ( echo 目录不存在跳过 goto :skip_temp )这个if not defined和if not exist的双重检查是我强烈建议每个人都加的。变量为空时rd /s /q %TEMP_DIR%会变成rd /s /q 行为不可预测加了判断就安全了。4. 完整脚本实操与逐段讲解4.1 脚本头部权限、编码、变量把前面几块拼起来脚本开头长这样echo off setlocal enabledelayedexpansion chcp 936 nul :: 管理员权限自检 net session nul 21 if %errorlevel% neq 0 ( echo 需要管理员权限正在提权... powershell -Command Start-Process %~f0 -Verb RunAs exit /b ) :: 定义清理路径 set USER_TEMP%TEMP% set SYS_TEMPC:\Windows\Temp set PREFETCHC:\Windows\Prefetch set UPDATE_CACHEC:\Windows\SoftwareDistribution\Download set RECYCLEC:\$Recycle.Binsetlocal enabledelayedexpansion是为了在循环里能实时读取变量变化清理多个目录时会用到。chcp 936是简体中文的代码页配合 ANSI 保存的脚本中文正常显示。4.2 清理用户临时目录echo. echo [1/6] 清理用户临时目录... if defined USER_TEMP if exist %USER_TEMP% ( del /f /q /s %USER_TEMP%\*.* nul 21 for /d %%d in (%USER_TEMP%\*) do rd /s /q %%d nul 21 echo 完成 ) else ( echo 跳过目录不存在 )这里先del删所有文件再用for /d遍历子目录逐个rd删掉。为什么不直接rd /s /q %USER_TEMP%再重建因为有些正在运行的程序会占用临时目录直接删整个目录可能失败逐个删子目录容错性更好。nul 21是把正常输出和错误输出都丢掉避免满屏报错。4.3 清理系统临时目录与更新缓存echo. echo [2/6] 清理系统临时目录... if exist %SYS_TEMP% ( del /f /q /s %SYS_TEMP%\*.* nul 21 for /d %%d in (%SYS_TEMP%\*) do rd /s /q %%d nul 21 echo 完成 ) echo. echo [3/6] 清理更新缓存... if exist %UPDATE_CACHE% ( del /f /q /s %UPDATE_CACHE%\*.* nul 21 echo 完成 )更新缓存这块我特意只删文件不删目录因为SoftwareDistribution目录本身是系统组件需要的删了可能导致更新服务异常。只清Download子目录里的内容就够了。4.4 清理预读取文件与回收站echo. echo [4/6] 清理预读取文件... if exist %PREFETCH% ( del /f /q /s %PREFETCH%\*.pf nul 21 echo 完成 ) echo. echo [5/6] 清空回收站... rd /s /q %RECYCLE% nul 21 echo 完成预读取文件我只删.pf后缀的这是程序启动预读缓存删了之后常用软件首次启动会慢一点点之后自动重建。不建议整个 Prefetch 目录清空保留系统自带的预读项更好。回收站用rd /s /q直接删$Recycle.Bin目录系统会自动重建。但这一步一定要放在最后并给用户确认因为回收站里可能有用户还没决定要删的东西。4.5 结果汇总与空间统计清理完给个反馈用户才知道有没有效果。统计释放空间稍微麻烦点简单做法是清理前后各查一次磁盘剩余空间for /f tokens3 %%a in (dir /-c %SystemDrive%\ ^| find 可用字节) do set BEFORE%%a :: ...执行清理... for /f tokens3 %%a in (dir /-c %SystemDrive%\ ^| find 可用字节) do set AFTER%%a不过这个统计受其他程序影响不一定准。更实用的做法是直接告诉用户“清理已完成”具体释放多少让用户自己看磁盘属性。我后来干脆不做精确统计了省得数字对不上反而让人怀疑。5. 常见问题与避坑经验实录5.1 清理后软件打不开、系统异常怎么办这是最吓人的情况但绝大多数时候不是脚本删错了而是删了某个正在运行的程序依赖的临时文件。解决办法很简单重启一次。重启后程序会重新生成需要的临时文件通常就恢复正常了。如果重启还不行回忆一下清理前有没有动过Prefetch或更新缓存。更新缓存删了不影响系统但如果你手动删了SoftwareDistribution整个目录可能需要重启更新服务。我个人的经验是只要严格按本文的路径清单来不自己乱加目录基本不会出问题。5.2 提示“拒绝访问”“文件正在使用”怎么处理清理时看到一堆“拒绝访问”是正常的因为很多临时文件正被程序占用。这些错误被nul 21吞掉了不影响脚本继续跑。如果你想看看到底哪些没删掉可以临时把21去掉让错误显示出来。对于确实想删但被占用的文件可以先用taskkill结束相关进程但这有风险可能丢数据。我的建议是被占用的就跳过下次重启后再清。可以在脚本里加一句提示告诉用户“部分文件被占用建议重启后再次运行”。5.3 常见问题速查表现象可能原因解决办法双击没反应没有管理员权限提权失败右键“以管理员身份运行”中文显示乱码脚本编码与控制台代码页不一致存为 ANSI或加chcp 936提示“系统找不到路径”路径变量为空或目录不存在加if exist判断清理后空间没变化清理的是小文件或文件被占用重启后再清或检查大文件目录脚本一闪而过执行完自动关闭窗口末尾加pause误删了重要文件路径写错或变量未定义加if defined保护先备份5.4 我踩过的三个真实坑第一个坑早期脚本里写了rd /s /q %TEMP%没加引号。结果某台机器上%TEMP%路径带空格命令被拆成两段删错了目录。从那以后我所有路径都加双引号。第二个坑用 UTF-8 保存脚本在英文系统上跑中文提示全变问号虽然不影响功能但用户看不懂。后来统一存 ANSI并在脚本里避免复杂中文。第三个坑有次为了多清空间把C:\Windows\Installer也加进去了结果好几个软件无法卸载和修复。这个目录存的是安装缓存绝对不能删。这个教训让我明白清理范围宁窄勿宽。6. 让脚本更安全、更好用的几个进阶技巧6.1 加一个“试运行”模式正式删之前先让脚本只打印要删什么、不真删确认无误再执行。实现方式是用一个变量控制set DRYRUN1 if %DRYRUN%1 ( echo [试运行] 将清理%USER_TEMP% ) else ( del /f /q /s %USER_TEMP%\*.* nul 21 )这个习惯救过我好几次尤其是改路径规则的时候先试运行看一眼心里有底。6.2 用日志记录每次清理把清理过程写进日志文件出问题能追溯set LOG%~dp0clean_log.txt echo [%date% %time%] 开始清理 %LOG%%~dp0是脚本所在目录日志跟脚本放一起方便找。日志里记录时间、清理了哪些目录、有没有报错比凭记忆靠谱。6.3 做成计划任务定期自动清理手动跑毕竟麻烦可以把它注册成计划任务每周跑一次。用schtasks命令schtasks /create /tn 定期清理 /tr %~f0 /sc weekly /d SUN /rl highest/rl highest是以最高权限运行避免权限不足。不过自动清理有个前提你的清理规则必须足够保守否则哪天误删了正在用的东西用户都不知道怎么回事。我一般建议自动清理只清临时目录和回收站其他目录手动跑。6.4 给不同用户定制不同清理范围家里老人的电脑主要清浏览器缓存和回收站开发机的电脑重点清各种构建缓存和日志。可以把清理项做成开关set CLEAN_BROWSER1 set CLEAN_DEV0需要哪项就打开哪项一个脚本适配多种场景不用维护好几份。7. 关于清理这件事我最后想说的写了这么多年脚本我越来越觉得清理系统垃圾的核心不是“删得多”而是“删得准”。网上那些一键清理工具之所以让人不放心就是因为它们追求清理量把很多不该删的也算了进去。自己写 bat 的最大好处就是你能完全掌控这个边界。我现在给自己电脑定的规则是临时目录每周清一次回收站随手清预读取和更新缓存一个月清一次其他一律不动。这套规则跑了两年多系统一直很稳也没出现过软件打不开的情况。脚本本身也就几十行放在桌面想清的时候右键管理员运行一下几秒钟的事。如果你刚开始写我的建议是先从只清%TEMP%开始跑顺了再逐步加目录每加一个都先试运行确认。别一上来就写个“全家桶”那样出问题的概率高排查起来也麻烦。清理脚本这东西稳比快重要得多。