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。
| fsearch | fff | |
|---|---|---|
| 按名称查找文件 | 1.1 ms | 13.8 ms |
| 在文件内容中搜索 | 5.6 ms | 53 ms |
| 拼写错误仍能率先找到文件 | 98% | 88% |
| 启动后就绪时间 | 50 ms | 2.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:是一个范围查询。每个不同的名称只评分一次。 - 内容搜索基于文本文件的三元组索引。匹配结果总是从磁盘实时读取,因此永远不会过时。