前言 - 我为什么又开始写博客了

最近对团队的前景比较悲观, 小作坊大厂化是主因. 结果就是连续几年持续亏损: 不看增长, 不看用户, 不看市场, 不看质量, 只求在自己的视野中得到所谓的“自我努力式感动”, 并且把能够做到“自我努力式感动”的人也拉入核心决策层. 说白了, 已不是一个拥有实际价值目标的团队.

回想最开始大家都是往一个目标去推进, 希望帮助用户的同时, 能够让团队成长, 让团队成员成长, 实现整体目标后, 团队中每个人的个人目标, 个人价值也得到实现, 并让团队所有成员时刻保持包容心和同理心, 这样能高效协作, 又可以让所有人都有充足动力. 现实和理想始终有差距, 不过差距是巨大的.

现在只求做好自己, 问心无愧. 让小丑站在舞台上表演, 当观众何尝不是一种乐趣, 或者说无奈. 以后有机会, 再选讲这种“小作坊大厂化”的现状和成因. 这也是我又开始写博客的原因, 关心的事情少了, 自然有更多时间思考现在和未来, 分享内容, 写博客, 做产品, 做开源, 把这些年的积累分享出来帮助更多的人.

为什么要清理电脑

说回正题, 正在进行 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 进行: 每一项先标注成如下四类

  1. 可再生: 缓存, 依赖, 构建产物, 直接删
  2. 可再生成但耗时: .venv, node_modules, Docker 镜像, 删了要重装
  3. 真实数据: 比如上面的 parquet, 聊天记录, 会话历史, 只归档不删
  4. 结构性保留: git 历史, 工具链本体, 最多做 gc

扫描流程

实际 AI 在做时, 有五步扫描, 每一步都用 df 验证增量:

bash
# 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/Developerdocker system prune
每两周到每月 dust -d 1 ~/Documents 找新增的 DerivedData 与 build; 不活跃仓库归档
每季度 dust -F 全盘最大文件复查; pnpm store prune; 清 .codex/sessions 旧月份
空间告急时 sudo tmutil deletelocalsnapshots /, TM 会重建快照, 属正常
长期习惯 不活跃仓库 git gc 后 tar 归档到外置盘; 大二进制不进 git, 走 LFS 或制品库

结论

  1. 最有效的三个动作: 删 TimeMachine 本地快照 (+22G), 删构建缓存目录群和用户缓存 (+35G 与 +31G), 清 /private/tmp 遗留 (+23G).
  2. git gc 只能回收约 1/3 的 git 目录体积 (9.6G 到 6.5G), 剩下的要重写历史, 通常不值得.
  3. 顺序比工具重要: 先分类, 再按 可再生 > 可再生成但耗时 > 真实数据 的顺序清理, 每步用 df 验证空间释放.
  4. AI 全程只用 CLI 工具, 没有把目录清单交给任何清理服务.

这些数字是单机单次记录, 仅示意流程, 说明并非只有高额付费软件才能进行高质量清理, AI 配合 dust 也可以做到, 且效果更好, 结果都是可验证可重复的.