Skip to content

11 · Analyze 磁盘分析器

Analyze 的核心不是“递归计算目录大小”,而是如何在结果仍不断到达时维持一个稳定、可信、可操作的 UI。

两种入口语义

未指定路径时进入磁盘概览,根锚定为 /;指定路径时进入目录浏览。--json 绕过 TUI 输出机器数据。程序以 Bubble Tea alternate screen 运行,model 同时持有当前条目、原始条目、过滤器、排序、按路径记录的多选、历史栈、缓存与 live scan 状态。

概览把 Home 与 User Library 分开显示,避免将 Library 既计入 Home 又单独展示而造成双计数。Applications、System Library 与洞察项也作为独立入口。

Cache-first 与 stale-while-revalidate

scanCmd 有三条路:

  1. 缓存新鲜:直接使用;
  2. 缓存过期:先展示旧数据,后台刷新;
  3. 无缓存:启动 live scan。

缓存包含 schema version、目录修改时间与扫描时间,并在启动/写入时做有界清理。硬链接结果因为依赖遍历顺序,不会持久化,避免缓存一个看似确定、实际由扫描顺序决定的归属。

Live scan 如何避免列表乱跳

扫描先快速读取直接子项,立即产生行;每个目标的大小作为 job 并行完成,通过事件回写。结果到达时可重新排序,但一旦用户移动光标,liveSortFreezeOnMove 冻结自动重排。选择集合以路径为 key,而不是以列表 index 为 key。

这两点共同解决实时列表的经典问题:新结果插入后,光标不能突然指向另一个文件,已勾选项也不能因排序变化而漂移。

扫描器的资源控制

扫描并发通过相互独立的 limiter 限制;大目录和大文件榜单使用 top-K 最小堆,而不是保存所有候选再全排序。符号链接只计链接本身,不跟随;根目录和默认规则跳过高风险/高成本位置;折叠目录先尝试有界 du,失败再递归。

稀疏文件和云文件使用 blocks * 512 与逻辑大小比较,尽量呈现实际占用。硬链接以 device + inode 去重。可选 Spotlight 扩展大文件发现,但不会成为正确性的唯一来源。

删除路径完全可恢复

Analyze 删除永远使用 Trash,不提供永久删除快捷键。它验证原始与绝对路径、关键系统目录、Home 根、Docker/OrbStack、EDR 和 symlink 解析目标。

路由顺序是:macOS 15+ 的 /usr/bin/trash → 当前卷正确 Trash 目录中的 renameatx_np(RENAME_EXCL) 原子移动 → Finder fallback。Trash 目录必须由当前用户所有,且不能向其他用户开放写权限。

删除完成后,程序使目标、当前目录与祖先缓存失效并重新扫描,避免 UI 用旧大小制造“文件还在”或“空间没释放”的错觉。

关键源码索引

下一章看另一个长期运行程序 Status:它如何在 1 秒刷新中避免自己成为被监控的负载。

独立源码研究笔记 · 非 Mole 官方文档