前言 - 我为什么又开始写博客了
最近对团队的前景比较悲观, 小作坊大厂化是主因. 结果就是连续几年持续亏损: 不看增长, 不看用户, 不看市场, 不看质量, 只求在自己的视野中得到所谓的“自我努力式感动”, 并且把能够做到“自我努力式感动”的人也拉入核心决策层. 说白了, 已不是一个拥有实际价值目标的团队.
回想最开始大家都是往一个目标去推进, 希望帮助用户的同时, 能够让团队成长, 让团队成员成长, 实现整体目标后, 团队中每个人的个人目标, 个人价值也得到实现, 并让团队所有成员时刻保持包容心和同理心, 这样能高效协作, 又可以让所有人都有充足动力. 现实和理想始终有差距, 不过差距是巨大的.
现在只求做好自己, 问心无愧. 让小丑站在舞台上表演, 当观众何尝不是一种乐趣, 或者说无奈. 以后有机会, 再选讲这种“小作坊大厂化”的现状和成因. 这也是我又开始写博客的原因, 关心的事情少了, 自然有更多时间思考现在和未来, 分享内容, 写博客, 做产品, 做开源, 把这些年的积累分享出来帮助更多的人.
为什么要清理电脑
说回正题, 正在进行 FSKit 扩展开发(仅 FSKit 层, 工作而非个人), 为保证实现正确性以及性能, 需要生成并存放各尺寸磁盘镜像, 安装多个系统版本虚拟机, 以及频繁运行选定的文件系统测试集(比如 apple fs tools, pjdfstests … ). 这种环境下自然要保证电脑有充足的可用空间, 自动化测试才能顺利进行(至少是开发侧, CI 机器上是不存在问题的). 开发机器的可用空间持续维持在 30G 以下, 系统持续显示已用 90%+. 之前一直没有时间做实际清理, 试过声称可以清理大量磁盘空间的付费产品, 但效果就是挠痒痒一样(比如一款声称简单高效清理产品, 实际清理效果只有 10GB+, 也就放弃了规则类软件清理这种路子).
专门抽了时间用 AI 配合 dust 进行了一次清理, 效果非常好, 且能形成脚本反复重用, 这样才是真正的高效清理体验, 并且也是许多 macOS 清理软件产品都值得去借鉴的, 真正提供给用户价值, 用户才会买单.
关于为什么要使用 dust: 之前调研过 dust 的结构, 非常简洁高效且适合磁盘空间分析, 而清理只需要几步判断和一轮动作.
下面的内容是使用 AI 根据实际情况进行总结生成(人写太慢, 但下方内容我进行了人工文字润色和人工复核), 用作记录和参考, AI 是高效的工具, 不要一味否定, 但也不要一味肯定, 工具是讲如何利用好, 而非一来就变成是非问题.
清理结果
- 可用空间 (
df -h /System/Volumes/Data): 30G 清到 216G - 系统显示的已用比例: 90%+ 降到 51%
- 净释放: 186G
- 工具: dust 1.2.4 (cargo 安装), 配合
df/du/tmutil/git gc/pnpm store prune
下面说说如何去做的.
先分类, 再动手
先让 AI: 使用 dust 按目录下钻, 总结并列出空间占用情况
运行结束后, 开发机器上的空间占用排序 (实测, 不代表普遍情况): 构建产物与依赖缓存 > 测试输出数据 > git 大文件历史 > AI 会话日志 > 应用缓存, 且比较分散.
分析后首次得出主要构成是(仅列出部分, 清理结果详见下方内容):
- 约 30G 是可再生依赖 (
.venv/node_modules/target/DerivedData等) - 约 25G 真实数据 (parquet dump, 这个是 GA4 导出到 Bigquery 再拉取的 parquet 数据, 用来作 app 行为分析)
- 约 13G git 历史
- 约 35G 应用数据
- 约 15G 工具链
然后让 AI 进行: 每一项先标注成如下四类
- 可再生: 缓存, 依赖, 构建产物, 直接删
- 可再生成但耗时:
.venv,node_modules, Docker 镜像, 删了要重装 - 真实数据: 比如上面的 parquet, 聊天记录, 会话历史, 只归档不删
- 结构性保留: git 历史, 工具链本体, 最多做 gc
扫描流程
实际 AI 在做时, 有五步扫描, 每一步都用 df 验证增量:
# 0. 总览: 谁在吃空间 df -h /System/Volumes/Data # 1. 顶层一层目录 dust -d 1 -n 25 /System/Volumes/Data # 2. 对最大目录逐层下钻 (-d 2 / -d 3) dust -d 2 -n 30 $HOME/Documents # 3. 找最大单文件 (-F 常被忽略, 但定位 parquet / pack / dmg 靠它) dust -F -n 25 $HOME/Documents # 4. 需要 sudo 的区域 (sudo 的 PATH 不含 ~/.cargo/bin, 必须写完整路径) sudo $HOME/.cargo/bin/dust -d 2 -n 30 /private/var # 5. 清理后再次确认 df -h /System/Volumes/Data
清理清单
有了上述结果后, 下一步就是让 AI 按顺序执行安全清理, 释放量与当时的可用空间汇总如下:
| # | 动作 | 命令要点 | 释放 | 可用 |
|---|---|---|---|---|
| 1 | TimeMachine 本地快照 9 个 | sudo tmutil deletelocalsnapshots /, 传挂载点即全删, 不必逐个 |
+22G | 52G |
| 2 | /private/tmp 遗留构建目录 134 个 |
先 ls -ld 确认归属是自己, 再 rm -rf |
+23G | 75G |
| 3 | macOS Install Data 2022 升级残留 |
sudo rm -rf "/System/Volumes/Data/macOS Install Data" |
+12G | 87G |
| 4 | Xcode / XcodeBuildMCP 的 DerivedData | 删 ~/Library/Developer/*/DerivedData, 保留 MCP worktrees 源码 |
+11G | 98G |
| 5 | 一个实验仓的构建缓存 (200 个 DerivedData-* 与 logs) |
保留 fixtures 与 md 文档 | +35G | 133G |
| 6 | 同一实验仓的测试输出 host-matrix (61 个运行目录) | 测试可重跑, 确认后删 | +17G | 150G |
| 7 | 单次会话的过程数据 2.2G | 删场景 .data, 保留全部 .md 结论文档 |
+3G | 153G |
| 8 | 用户级缓存群 | ~/.cache; ~/.gocache (需先 chmod); ~/.npm; .nvm 旧版本只留最新; .codex/sessions 删旧月份; mediaanalysisd 分析缓存 |
+31G | 184G |
| 9 | Docker | 先启动 Docker Desktop, docker system prune -a -f (保留卷) |
+3G | 187G |
| 10 | 应用缓存 | ~/Library/Caches 各大项 (Chrome / SwiftPM / Playwright / go-build); ~/.bun/install/cache; pnpm store prune |
+7G | 194G |
| 11 | git gc | 11 个不小于 300M 的 .git 逐个 git -C <repo> gc --quiet |
+3G | 197G |
| 12 | 全仓可再生依赖 (264 个目录) | .venv / venv / node_modules / target / .dart_tool / Pods / DerivedData / 普通 build, 含发布产物的 build 跳过 |
+19G | 216G |
没清的部分
有一些是人工评估需要保留不能删除的:
- 4 个含已签名发布产物的
build目录, 约 4G - git 历史硬块约 10G (单个仓库最大 1.27G): 已 pack 的二进制 blob, 只有
git filter-repo重写历史才能减, 代价不划算 - 21G parquet 原始数据与 3.8G 备份
~/Library/Application Support下的编辑器, 浏览器 profile 与 IM 数据: 用户数据~/.local/share/uv(tools 1.3G + python 110M): 这里是已安装的工具, 不是缓存, 删了会坏- SIP 保护的
macOS Install Data/Locked Files残壳 12K: 只有 Recovery 模式能删, 没有空间意义
踩的坑
| 坑 | 原因 | 解法 |
|---|---|---|
sudo dust 报 command not found |
sudo 的 secure_path 不含 ~/.cargo/bin |
用完整路径 sudo $HOME/.cargo/bin/dust |
dust 输出一大串 No such file or directory |
SIP 保护目录读不到 | 无害噪音, 统计不受影响 |
删升级残留报 Operation not permitted |
文件带 restricted 标志 (/bin/ls -lO 可见), 属 SIP 保护 |
系统内无解, rm 会先删掉不受保护的部分, 残壳留着或去 Recovery 删 |
.gocache 删不掉, 大量 Permission denied |
Go module cache 的文件是只读的 | chmod -R u+rwX ~/.gocache && rm -rf ~/.gocache |
删 Chrome 缓存报 Directory not empty |
Chrome 运行中持有文件 | 再跑一次, 或先退出 Chrome |
docker prune 后空间没全回来 |
Docker.raw 是稀疏文件, 只增不完全缩 |
prune 后实际分配会缩 (本机 7.5G 到 3.7G), 彻底回收要删 VM 重建 |
tmutil deletelocalsnapshots 逐个删太慢 |
该命令接受挂载点 | sudo tmutil deletelocalsnapshots / 一次删光 |
找不到 macOS Install Data |
位置在 Data 卷根, 不在 / |
真实路径 /System/Volumes/Data/macOS Install Data |
ls -O 报错 |
shell 里 ls 被 GNU 别名覆盖 |
用 /bin/ls -lO |
删 ~/.local/share/uv 会坏工具 |
里面是已装 CLI 与 Python 解释器 | uv 的缓存在 ~/.cache/uv |
build 目录一刀切会误删发布产物 |
仓里的 build 混有 publish / sparkle 包 | 删前看子目录名, 含 publish / direct-distribution / sparkle-release 的跳过 |
mediaanalysisd 缓存 3.6G 看着吓人 |
Apple 照片与视频的场景分析缓存 | 删 Data 卷下对应目录是安全的, 系统按需重建 |
维护节奏
形成可重用的流程和脚本均可, 非强制维护, 到时间看需要清理就清理一次即可.
| 频率 | 动作 |
|---|---|
| 每周 | df -h 看一眼; 低于 50G 时 dust -d 1 ~/Library/Developer 加 docker system prune |
| 每两周到每月 | dust -d 1 ~/Documents 找新增的 DerivedData 与 build; 不活跃仓库归档 |
| 每季度 | dust -F 全盘最大文件复查; pnpm store prune; 清 .codex/sessions 旧月份 |
| 空间告急时 | sudo tmutil deletelocalsnapshots /, TM 会重建快照, 属正常 |
| 长期习惯 | 不活跃仓库 git gc 后 tar 归档到外置盘; 大二进制不进 git, 走 LFS 或制品库 |
结论
- 最有效的三个动作: 删 TimeMachine 本地快照 (+22G), 删构建缓存目录群和用户缓存 (+35G 与 +31G), 清
/private/tmp遗留 (+23G). git gc只能回收约 1/3 的 git 目录体积 (9.6G 到 6.5G), 剩下的要重写历史, 通常不值得.- 顺序比工具重要: 先分类, 再按
可再生 > 可再生成但耗时 > 真实数据的顺序清理, 每步用df验证空间释放. - AI 全程只用 CLI 工具, 没有把目录清单交给任何清理服务.
这些数字是单机单次记录, 仅示意流程, 说明并非只有高额付费软件才能进行高质量清理, AI 配合 dust 也可以做到, 且效果更好, 结果都是可验证可重复的.