干我们这行的,电脑上最不缺的就是临时文件。项目跑完,日志堆了一地;编译一次,中间产物比源码还大;IDE开两天,索引缓存直接把磁盘塞满。平时懒得管,等C盘飘红、磁盘空间告急,才知道“临时文件自动化清理”这五个字有多值钱。这篇内容适合所有经常被磁盘空间困扰的开发者——不管你是天天打交道的Windows用户,还是习惯Linux服务器的后端,我尽量把思路、代码、坑都写清楚,你照着改就能用。主题就一个:写脚本把临时文件管起来,安全删,删完告诉你释放了多少空间。
1. 临时文件为什么越攒越多,清理为什么必须自动化
1.1 开发者设备上的临时文件从哪来
先盘一下家底。开发者电脑上的临时文件,来源比你想的多得多:
- 系统临时目录:Windows的
C:\Users\<用户名>\AppData\Local\Temp,Linux的/tmp和/var/tmp,macOS的$TMPDIR,这里面既有安装包解压残留,也有程序崩溃时留下的dump文件。 - Windows 更新缓存:
C:\Windows\SoftwareDistribution\Download,这个目录专门存放Windows更新下载的安装包,系统更新装完以后,里面的文件基本就是纯废物,却能轻松占用几个GB。 - 软件日志:各种应用的
logs目录,尤其是开发工具,一天能写几十MB日志。Debug级别跑起来,日志文件增长速度快到你怀疑人生。 - 应用缓存:浏览器的Cache,Node的
npm cache,Python的pip cache,Go的go build缓存,Git的.git对象残留,还有Electron系应用的Cache目录,这些都是“看起来没用但程序就是不开源删”的数据。 - 回收站遗留:你以为删掉的文件就消失了?没有,它们只是躺在回收站里继续占着磁盘空间。我见过同事的回收站里躺着80GB的虚拟机镜像。
- 构建产物与依赖缓存:
node_modules里的.cache、target、dist、build这些目录,清理的时候要小心,但它们确实会占用大量空间。
每个来源看起来都不起眼,一旦叠加起来就很可观。我之前帮同事清一台开发机,光是AppData\Local\Temp就找到12GB的Visual Studio编译缓存,加上npm和pip的缓存,一次性释放了接近30GB。
1.2 为什么要写脚本而不是手工清理
有人会问,Windows不是自带“磁盘清理”工具吗?手动点几下不就行了?
问题是,手工清理有三个绕不开的痛点:
第一,你永远记不住上次什么时候清的。临时文件是持续产生的,今天清完,明天项目一跑又涨回来。磁盘清理工具属于“出了问题才想起来用”的方案,等到想起来的时候,C盘早就飘红了。
第二,手动清理容易漏。系统自带的磁盘清理只覆盖部分目录,像pip cache、npm cache、IDE索引、WSL虚拟磁盘里的临时文件,它根本不会管。你得挨个目录去翻,翻漏一个就等于没清干净。
第三,手动清理容易误删。很多开发者的“清理”就是打开C盘一顿删,删到什么不该删的,第二天发现项目启动不了,那才叫欲哭无泪。脚本就不一样,规则可以提前定死,什么能删、什么必须留,写清楚之后交给机器执行,比人肉判断靠谱得多。
我们写自动化清理脚本,解决的就是这三个问题:定时跑、范围全、规则严。脚本跑完还能自己统计释放了多少空间,不用你猜。
2. 自动化清理的设计思路与选型
2.1 清理范围划定:哪些能删、哪些绝不能碰
写脚本的第一步不是写代码,而是画红线。我自己的原则是:宁可漏删,不可误删。开发机上的文档、照片、源码、安装软件这些都是真金白银,清错了代价太高。
需要划进清理范围的,总结起来就几类:
| 清理对象 | 典型路径 | 备注 |
|---|---|---|
| 系统临时文件 | Windows%TEMP%,Linux/tmp、/var/tmp | 删除时可跳过正在被占用的文件 |
| Windows更新缓存 | C:\Windows\SoftwareDistribution\Download | 删除前建议先停掉Windows Update服务 |
| 软件日志 | 应用logs目录,注意日志轮转 | 按修改时间删,超过30天再清 |
| 应用缓存 | npm cache、pip cache、go build缓存 | 用工具自带命令清,别直接删整个目录 |
| 回收站冗余文件 | Windows回收站、Linux~/.local/share/Trash | 直接清空,无需保留 |
| 构建残留缓存 | node_modules\.cache、.gradle、.m2仓库旧版本 | 保留当前项目依赖,只清过期缓存 |
| Electron类应用缓存 | workbuddy、VS Code等工具的缓存目录 | 按具体应用目录定向处理 |
绝不能碰的:个人文档目录、照片库、数据库数据目录、虚拟机磁盘镜像、正在运行的Docker容器数据卷、.git目录本身。这些东西一旦删除,轻则项目回归,重则数据全无。我的方案里,所有删除范围都严格限定在“缓存类目录”和“临时目录”,绝不递归扫描用户文档、桌面、图片这些位置。
2.2 脚本语言与运行方式的选择
清理脚本用什么语言,主要看你的使用场景:
- Windows 主力机:PowerShell 是首选。它调用Windows原生API方便,可以完美处理权限问题、文件属性和回收站操作。不要用批处理,写起来绕,处理长路径和特殊字符会想死。如果你机器上有Git,用Git Bash执行Bash脚本也行,但权限处理和路径格式要额外处理。
- Linux/macOS 服务器或开发机:Bash 一把梭,systemd timer + cron 定期执行。Bash 处理文件循环、查找、删除都很顺手,脚本依赖少,任何发行版都能跑。
- 跨平台统一管理:如果你想在Windows和Linux上共用一套逻辑,可以用Python,配
pathlib处理路径,但要记得处理权限、文件锁定等平台差异,工程量会大一些。
我个人的习惯是:Windows上做用户态清理用PowerShell,Linux服务器上用Bash,各司其职,不强行统一。理由很简单——每个平台的临时文件机制差异太大,强行写一套跨平台脚本,最后的结果往往是两边的坑都踩一遍。
2.3 白名单与黑名单机制设计
清理脚本的规则设计,核心是三层过滤:
第一层:目录白名单。只允许脚本扫描预设的几个目录,不在白名单里的目录一律不碰。比如Windows上只扫描$env:TEMP、SoftwareDistribution\Download、C:\Windows\Prefetch这些明确是缓存/临时性质的位置。这个白名单可以有效防止脚本路径写错时把用户目录给清空。
第二层:文件黑名单。对于某些目录,虽然整体可以清理,但里面可能有需要保留的文件。比如缓存目录里的debug.log你可能正在排查问题,刚写好的patch文件也许还在用。我会在脚本里记录一个“最近7天修改过”的例外列表,这个列表里的文件跳过删除。
第三层:预演模式(Dry-Run)。所有脚本都必须支持一个-DryRun参数,只列出要删的文件和释放空间估算,不实际删除。这个模式太重要了,我第一次写清理脚本时,直接全量跑,结果把刚下载还没安装的安装包全删了,心疼了好一阵。预演模式跑一遍,确认无误再实际执行,是排查误删风险的底线。
3. 核心清理逻辑与代码实现
3.1 Windows 平台:PowerShell 自动化清理脚本
Windows端我写过一个比较完整的PowerShell脚本,核心思路是:先按目录清理,每删一个文件就把大小记下来,最后统计总释放空间。
# Invoke-Cleanup.ps1 param( [switch]$DryRun, [int]$DaysOld = 30 ) $startFree = (Get-PSDrive C).Free $totalFreed = 0 $skipped = @() function Remove-OldFile { param( [string]$Path, [int]$DaysOld, [bool]$DryRun ) if (-not (Test-Path $Path)) { return } Get-ChildItem -Path $Path -Recurse -Force -ErrorAction SilentlyContinue | Where-Object { -not $_.PSIsContainer -and $_.LastWriteTime -lt (Get-Date).AddDays(-$DaysOld) } | ForEach-Object { $size = $_.Length if ($DryRun) { Write-Host "[DryRun] 将删除: $($_.FullName) ($([math]::Round($size/1MB,2)) MB)" } else { try { Remove-Item -LiteralPath $_.FullName -Force -ErrorAction Stop $script:totalFreed += $size } catch { $script:skipped += $_.FullName } } } } # 1. 用户临时目录 Remove-OldFile -Path $env:TEMP -DaysOld $DaysOld -DryRun $DryRun # 2. Windows 更新缓存(需要管理员权限) $updateCache = "C:\Windows\SoftwareDistribution\Download" if (Test-Path $updateCache) { Remove-OldFile -Path $updateCache -DaysOld $DaysOld -DryRun $DryRun } # 3. 清空回收站 if (-not $DryRun) { try { Clear-RecycleBin -Force -ErrorAction SilentlyContinue Write-Host "回收站已清空" } catch { Write-Host "回收站清理失败: $($_.Exception.Message)" } } # 4. 统计结果 $endFree = (Get-PSDrive C).Free $diff = [math]::Round(($endFree - $startFree) / 1MB, 2) Write-Host "==== 清理报告 ====" Write-Host "本次清理释放空间: $($diff) MB" Write-Host "跳过占用文件数量: $($skipped.Count)"这个脚本有几个细节值得说道说道:
-ErrorAction SilentlyContinue配合-ErrorAction Stop结合使用,是为了让单个文件被占用时不影响整体执行。不被占用的文件正常删,被占用的跳过,最后报告里有记录。- 回收站用
Clear-RecycleBin -Force,比手动点击“清空回收站”靠谱,不会弹确认框。 - Windows更新缓存目录删除时,如果更新服务正在运行,部分文件会被锁定。实测中我遇到过删除到一半报“正在被另一个进程使用”的情况,归属到
skipped数组做个标记就好,不影响大局。彻底清理可以配合sconfig或服务停止,但作为日常清理,不需要每次都做那么重的操作。
注意:Windows脚本涉及
C:\Windows\SoftwareDistribution时需要管理员权限,建议右键“以管理员身份运行”。不想每次右键的话,可以给脚本创建一个快捷方式,勾选“以管理员身份运行”,或者用任务计划程序配置最高的权限运行。
3.2 Linux/macOS 平台:Bash 自动化清理脚本
Linux端的清理逻辑和Windows大同小异,但有几个平台特有的点:/tmp目录有sticky bit,删除时不能删目录本身,要删目录里的内容;日志清理要优先用logrotate的产物;npm、pip、go这些工具缓存有各自的清理命令。
#!/usr/bin/env bash # cleanup-temp.sh set -uo pipefail DRY_RUN="${1:-}" START_FREE=$(df -P / | awk 'NR==2 {print $4}') TOTAL_FREED=0 DAYS_OLD=30 log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" } safe_remove() { local path="$1" local days="$2" if [ ! -e "$path" ]; then return 0 fi while IFS= read -r -d '' file; do local size size=$(stat -c %s "$file" 2>/dev/null || echo 0) if [ "$DRY_RUN" = "dry-run" ]; then log "[dry-run] 将删除: $file ($((size / 1024 / 1024)) MB)" else if rm -f "$file" 2>/dev/null; then TOTAL_FREED=$((TOTAL_FREED + size)) else log "跳过被占用文件: $file" fi fi done < <(find "$path" -type f -mtime "+$days" -print0 2>/dev/null) } # 1. 清理系统临时目录内容 safe_remove "/tmp" "$DAYS_OLD" safe_remove "/var/tmp" "$DAYS_OLD" # 2. 清理用户缓存目录 safe_remove "${HOME}/.cache" "$DAYS_OLD" # 3. 清理应用缓存 npm cache clean --force 2>/dev/null && log "npm 缓存已清理" pip cache purge 2>/dev/null && log "pip 缓存已清理" go clean -cache 2>/dev/null && log "go 构建缓存已清理" # 4. 清理 journal 日志,保留最近 100MB journalctl --vacuum-size=100M 2>/dev/null && log "journal 日志已收缩到 100MB 以内" # 5. 清理回收站 rm -rf "${HOME}/.local/share/Trash/"* 2>/dev/null && log "回收站已清理" END_FREE=$(df -P / | awk 'NR==2 {print $4}') DIFF=$(( (END_FREE - START_FREE) / 1024 )) log "==== 清理报告 ====" log "本次清理释放空间: ${DIFF} MB"这里面有几个容易踩的坑:
set -e千万慎用。清理脚本里很多命令本来就是“找得到就删,找不到就算了”,如果开了set -e,遇到某个目录不存在直接退出,后面的逻辑全废了。我用的set -uo pipefail,不启停错误就退出,配合命令内部的|| true或2>/dev/null来兜底。find ... -mtime +30的意思是“修改时间在30天之前的”。这个参数在你需要调整清理周期时很有用,但要注意,日志文件如果一直在被写入,mtime会一直更新,也就是说“正在运行的进程写的日志能一直躲过30天规则”,这其实是好事,避免删除正在写入的文件。journalctl --vacuum-size=100M是清理systemd日志的专用接口。直接删/var/log/journal目录下的文件容易出问题,但用日志系统自己的接口先收缩大小,再让journald重启后自动生效,就稳妥得多。
3.3 workbuddy 等应用缓存与日志的定向清理
除了系统层面的清理,开发者机器上真正占用空间的往往是应用自己的缓存目录。就拿热词里提到的workbuddy来说,这类工具一般会在用户目录下存放对话记录、运行缓存和临时文件,体积动辄几个GB。关键是,这些目录往往不在系统临时目录里,常规清理根本扫不到。
我的做法是给脚本增加一个“定向清理”阶段,专门扫描已知应用的缓存目录:
# 定向清理:Electron/Tauri 等应用的用户数据缓存目录 function Clear-AppCache { param( [string]$AppPath, [bool]$DryRun ) if (Test-Path $AppPath) { Write-Host "发现应用缓存目录: $AppPath" Remove-OldFile -Path $AppPath -DaysOld $DaysOld -DryRun $DryRun } } $appDirs = @( "$env:APPDATA\workbuddy\Cache", "$env:LOCALAPPDATA\workbuddy\Cache", "$env:LOCALAPPDATA\workbuddy\Code Cache", "$env:LOCALAPPDATA\Programs\WorkBuddy\Cache", "$env:APPDATA\Code\Cache", # VS Code "$env:APPDATA\Code\CachedData" ) foreach ($dir in $appDirs) { Clear-AppCache -AppPath $dir -DryRun $DryRun }这里有两点要特别注意:
第一,不能整个目录直接删。像workbuddy的目录里除了缓存,还可能有用户配置、会话记录之类的数据,直接删根目录等于把配置也丢了。所以上面的代码只进到Cache、Code Cache这类明确是缓存性质的子目录,保留Config、Local Storage等数据目录不动。
第二,有些缓存目录的文件本来是分布式存储的,比如 SQLite 数据库的分片。直接删文件可能导致应用读不到历史会话。稳妥的做法是只删除超过30天未被修改的文件——还是配合那个-DaysOld 30的开关,宁少勿滥。如果应用自身带“清理缓存”按钮,我优先推荐先用手动功能,脚本只做兜底。
3.4 空间释放统计与报告
所有脚本清理完成后,最有感知的反馈就是“到底释放了多少空间”。这里有两种统计维度,各有用途:
维度一:系统整体空间变化。清理前后各查一次磁盘总剩余空间,相减得到差值。Windows用Get-PSDrive C,Linux用df -P /。这个数字最直观,但会受到程序并发写入的干扰——清理过程中如果其他进程在写磁盘,结果会偏小;如果有大文件刚好在清理时被删除,结果又会偏大。作为“报告给用户看的数字”完全够用。
维度二:实际累积的文件大小。每删一个文件,就把length累加到一个变量里。这个数字更准确,能告诉你脚本到底“处理了多少数据”。我在两个脚本里都加了TOTAL_FREED或$totalFreed变量,就是为了保留这个信息。
我实际跑完一份报告大概长这样:
==== 清理报告 ==== 本次清理释放空间: 1836.42 MB 跳过占用文件数量: 17 预热报告: 已删除临时文件 2418 个,总大小 1836.42 MB报告放在脚本最后,也可以结合计划任务在清理完成后把报告输出到日志文件。Windows的计划任务写法不在这里展开,但Linux端如果你用cron,只需一行:
0 3 * * 6 /home/user/scripts/cleanup-temp.sh >> /home/user/logs/cleanup.log 2>&1每周六凌晨3点跑一次,日志沉淀下来,慢慢你就能看到自己设备的临时文件增长规律了。
4. 验证机制与常见问题排查实录
4.1 清理结果怎么验证
脚本跑完不代表任务结束,验证是必须的。我的习惯是验证三件事:
第一,文件数变化。清理后在临时目录里再数一遍文件数和总大小,确认清理生效。Windows可以用系统属性看目录大小,Linux用du -sh /tmp,如果大小明显下降,说明清理真的干了活。反之,如果大小没变,说明你设置的DaysOld参数太保守,或者文件全被占用。
第二,系统与应用的功能性。清理完之后,至少要让工作流跑一遍。我是这么验证的:打开IDE编译一次项目,启动几个常用程序,跑一次日常脚本。如果程序能正常启动、项目能正常编译、日志里不报错,基本上说明没有误删关键文件。清理脚本最大的风险不是“删不干净”,而是“删过头”,所以功能验证步骤不能省。
第三,空间数值对账。清理报告里显示的“释放空间”和系统实际剩余空间变化,两者允许有误差,但误差应该在几百MB以内。如果报告说释放了5GB,实际剩余空间却只多了1GB,那说明清理过程中有进程重新生成了大量缓存,或者你的脚本统计逻辑有问题,需要回去查。
4.2 常见坑与排查实录
结合我自己的踩坑经历,整理几个高频问题:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 脚本提示“拒绝访问” | 没有管理员权限,或目标文件是系统文件 | 右键以管理员身份运行;对单个文件加入跳过列表 |
| 删除到一半报“正在使用” | 文件被正在运行的进程锁定 | 记录到跳过列表,不阻塞整体执行;下次在应用退出后运行 |
| PowerShell提示“禁止运行脚本” | 系统执行策略默认限制脚本运行 | 用Set-ExecutionPolicy RemoteSigned -Scope CurrentUser放开当前用户权限 |
| 清理后软件重新生成缓存 | 应用缓存目录本身就是应用运行时必需的 | 调整策略,将当前正在使用的应用目录从清理范围中排除,只清理过期缓存 |
Linux清理/tmp后应用变慢 | /tmp里有应用依赖的临时文件 | 调整DAYS_OLD为7,只删除“一周没动过”的文件 |
| Windows更新缓存删不掉 | Windows Update服务还在运行 | 先net stop wuauserv,清理完再net start wuauserv |
| 硬盘剩余空间没变化 | 回收站未清理;或删除的文件还没触发磁盘整理 | 清空回收站;检查是否有其他进程占用文件;必要时重启再查看 |
| 误删了刚下载的安装包 | 手工清理时没注意修改时间 | 所有新增删除规则必须带“按修改时间过滤”,刚下载的文件mtime是今天,就不会被-DaysOld 30扫到 |
4.3 防止误删与回滚方案
最后说一个很多人不重视但我觉得极其重要的事:任何自动化清理都必须有后悔药。
我常用的回滚方案是在Remove-Item之前先对删除的文件做一次“软删除”——把文件移动到一个备份目录而不是直接删除。虽然会占用一部分临时空间,但对于重要度比较高的工作机,心理踏实很多。
PowerShell里的做法是把Remove-Item换成Move-Item到回收站或备份目录,Linux下则是mv到一个.trash隐藏目录。比如:
# 软删除方案:移动到备份目录,定期自动清空备份目录 $backupRoot = "C:\CleanupBackup" Move-Item -LiteralPath $file.FullName -Destination $backupRoot -Force这个方案适合第一次跑脚本、对规则还不够信任的阶段。等脚本稳定了,确认从没误删过,再改成直接Remove-Item也不迟。
另外,每个清理脚本都必须支持-DryRun或dry-run参数,这是底线。我一直把预演模式当成“安全开关”,任何规则修改之后都先在预演模式跑一次,看看将要删除的文件列表里有没有不该删的东西,然后再正式执行。
5. 结合个人经验的一点建议
写清理脚本这件事,技术上不算难,难的是对设备和数据有敬畏心。我在实际维护这套脚本的过程中,最大的体会是:别追求“最全清理”,要追求“稳定可重复”。你真正需要的是一个每周都能跑、从不误删、跑完能告诉你结果的机制,而不是一次性能清出80GB但哪天不小心把配置清掉的定时炸弹。
刚开始可以从最简单的单个目录做起,比如只清Temp目录,加上-DaysOld 7参数,跑两周看效果,再慢慢扩展清理范围。每新增一个清理目录,都要先问自己一句:这个目录里的数据删了,我确信不会影响任何正在进行的开发工作吗?如果有一丝犹豫,就先加白名单,再用预演模式验证,然后才放开实际删除。
脚本跑顺了以后,可以给它加个定时任务,Windows用任务计划程序,Linux用cron或systemd timer,再配一份简单的日志输出,就能做到“无感清理”。等哪一天磁盘告急的时候,你翻出日志来看,发现脚本已经默默维护这片战场很久了,你会习惯这种感觉。