ESC
开源 1 分钟阅读

fsearch:macOS 全盘文件搜索工具——模糊名称、拼写容错、索引内容检索,800万文件约1毫秒

开源项目 fsearch 为 macOS 提供全盘文件搜索:按名称查找约1毫秒完成,支持模糊匹配与拼写容错,并可基于三元组索引搜索文件内容。在 M4 Max 上对770万文件的名称检索 p50 仅1.3ms,文件变动约0.1秒内可见。可作为 CLI、守护进程或 Rust crate 使用,对比同类工具 fff 在速度与内存占用上优势明显。

来源:GitHub

FSearch

macOS 全盘文件搜索工具。按名称查找任意文件只需约一毫秒,容忍拼写错误,并通过索引搜索文件内容。可作为 CLI(附带一个小型守护进程)使用,也可作为 Rust crate 使用。

cargo build --release && ./target/release/fsearch install   # -> ~/.local/bin/fsearch
fsearch fsearch main              # 按名称查找文件
fsearch 'ext:rs grep:apply_dir'   # 在文件内容中搜索

速度

M4 Max,磁盘上共770万个文件和文件夹。

全盘按名称查找文件p50 1.3 ms
在文件内容中搜索p50 9 ms
新建、重命名或删除的文件可见时间~0.1 s
首次爬取磁盘约20秒,仅需一次
守护进程内存占用30-135 MB

与 fff 对比

Chromium(50.9万个文件),同一台 Mac,相同的查询。视频:demo/fsearch-vs-fff.mp4,测试方法:demo/vs_fff.py。

fsearchfff
按名称查找文件1.1 ms13.8 ms
在文件内容中搜索5.6 ms53 ms
拼写错误仍能率先找到文件98%88%
启动后就绪时间50 ms2.5 s
内存占用50 MB(整个磁盘)358 MB(仅该文件夹)

在更小的 Linux 内核代码库(9.6万个文件)上,名称搜索打平,其余项目 fsearch 胜出。fff 能搜索的内容文件多约9%,因为 fsearch 会跳过某些文件类型以及 build/ 和 vendor/ 文件夹。

查询语法

fsearch 'readme in:~/Developer'          # 在某个文件夹内搜索
fsearch 'type:image size:>5mb mtime:<7d'
fsearch 'ext:rs regex:fn\s+\w+_dir'      # 在文件内容中使用正则
fsearch 'sym:apply_dir'                  # 查找定义位置

单词支持模糊匹配,5个字母以上的单词可容忍一个拼写错误(mian.rs 能找到 main.rs)。此外还支持 'exact(精确匹配)、^prefix(前缀)、suffix$(后缀)和 !exclude(排除)。过滤器:ext: type: kind: in: size: mtime: re: path: grep: regex: sym: limit:。内容搜索支持智能大小写。

完整磁盘访问权限

从拥有「完整磁盘访问权限」的终端启动时,它会索引所有内容。作为登录项运行(fsearch install --login)时,需要在「系统设置 > 隐私与安全性」中单独为 ~/.local/bin/fsearch 授予权限,且每次重新构建后都需重新授权。没有权限时,它会跳过受保护的文件夹,而不会弹出授权提示。

API

通过 ~/Library/Application Support/FSearch/fsearch.sock 使用 JSON 行协议,或使用 fsearch stdio:

{"q": "fsearch main", "limit": 20}
{"op": "grep", "pattern": "apply_dir", "in": "~/Developer"}

也可以直接链接该 crate:

let engine = fsearch::Engine::start(fsearch::Options { dir: fsearch::default_dir(&home), home: home.clone(), skip: None })?;
let hits = engine.search(&fsearch::Query::parse("fsearch main", &home)?)?;

应用与 CLI 共享同一个索引:第一个进程持有它,其余进程跟随使用。

工作原理

  • 使用 getattrlistbulk 对磁盘做一次完整爬取,随后通过 FSEvents 保持实时更新。重启时仅重放发生变化的部分。
  • 文件名存放在单个 mmap 文件中,按文件夹布局,因此 in: 是一个范围查询。每个不同的名称只评分一次。
  • 内容搜索基于文本文件的三元组索引。匹配结果总是从磁盘实时读取,因此永远不会过时。