开源 fsearch:Rust 写的 macOS 全盘搜索,文件名 0.1 毫秒,拼错了也能找到

用 Mac 的朋友大概率遇到过这种场面:机器用了两三年,文件攒到几十万个,某天忽然想找三个月前改过的一份配置,Spotlight 那个放大镜转啊转,转完弹出来的东西还经常不是你要的那一个。

更憋屈的是搜内容。Spotlight 对文件正文的支持基本属于"有,但别指望";自带的 find 倒是肯翻全盘,可等你一碗泡面泡好了它还在跑;专业的 ripgrep 快是真快,但它默认只搜当前目录,想全盘 grep 得自己从头写一堆排除规则。

还有一类尴尬特别真实:你明明记得那文件叫 mian.rs——别笑,拼错文件名是人间常态——但几乎所有搜索工具都默认你的输入完全正确,拼错一个字母,结果就是干干净净的零。

把这些需求摞在一起看:全盘范围、毫秒级响应、文件名和内容双模式、索引还得跟着文件系统实时更新。你会发现这不是"换个搜索工具"能解决的问题,这背后要的是一整套常驻本地的索引引擎。

为什么难?因为这里有一组天然互斥的矛盾:索引要全,就得把盘上每个文件都管起来,索引体积小不了;要新,就得实时感知文件系统里每一次创建、改名、删除;要快,还得在百万级文件里把查询延迟压到人感觉不到的数量级。市面上的方案各解决了一半:系统自带搜索索引不透明、排序逻辑像个黑箱;命令行工具要么全盘硬扫、要么只管当前目录;云盘检索倒是快,但你的文件得先上传。今天要聊的开源项目 fsearch,就是冲着这个组合拳来的。它的做法很直接:用 Rust 写一个常驻 macOS 的全盘搜索引擎,把"快"做成默认值。

它是什么:一个跑在本地的小引擎,三种形态

fsearch 是一个用 Rust 编写的 macOS 全盘搜索工具。按官方 README 的说法,它要做到的是:按文件名查询约 0.1 毫秒、容忍拼写错误、外加对文件内容的索引化搜索。

它有三种使用形态,覆盖三种人:

  • CLI + 守护进程:装完之后在终端里直接敲 fsearch,后台有一个小守护进程常驻,索引由它维护;
  • Rust crate:可以把引擎嵌进你自己的程序,给文件管理器、命令面板、编辑器插件提供搜索能力;
  • MCP 服务器:跑一个 fsearch mcp,你的 AI 编程助手就多了 find_files、grep、multi_grep 三个工具,可以直接检索你整台机器的文件。

一句话概括:它把"全盘索引 + 毫秒查询 + 内容检索"打包成了一个跑在本地的小引擎,人和 AI 都能使唤,数据不出本机。

项目采用 MIT 协议,源码托管在 GitHub:github.com/noahdunnagan/fsearch。

fsearch 工作原理示意图:全盘文件汇入常驻本地索引引擎,毫秒级查询实时命中

核心功能拆解

一、文件名搜索:拼错两个字母都拦不住你

fsearch 最抢眼的能力是模糊匹配。按 README 的说法,查询词在任意位置拼错一个字母能容忍,8 个字母以上的词甚至容忍两个拼写错误——输入 mian.rs 能找到 main.rs。多个词可以拼出同一个文件名,带重音的名字无论按哪种形式存储都能匹配上。当然它也支持精确模式:'exact、^prefix、suffix$、!exclude 这些写法都在。

官方给出的基准数据(以下均为官方自测数据,测试机是 M4 Mac mini,数据集是本机的 Linux 内核和 Chromium 源码树):查询结果"第一个就是对的"比例,优化前是 83% / 86%,现在达到 93% / 97%;如果查询词带两个拼写错误,这个比例从 11% / 15% 提升到 74% / 93%。

顺带说一句延迟:同样这两个源码树,按名字找到一个文件的中位耗时是 0.054 / 0.12 毫秒。这个数字是官方口径,具体机器和数据集会直接影响结果,但它至少说明了一件事——在这种规模下,查询本身已经不该是等待的来源了。

支撑这个速度的底层设计也有看点。所有文件名存在一个 mmap 的内存映射文件里,按文件夹分区,所以 in: 这种范围过滤天然就是一次区间查询,不需要遍历;每个名字还配了一个字母位图,打分之前先把大部分名字筛掉。再加上查询词可以拆成多个词去拼同一个文件名、带重音的名字按任意存储形式都能匹配,日常"记不全、记不准"的搜索习惯被照顾得相当周到。

二、内容搜索:三元组索引加预过滤,只读"可能命中"的文件

光有文件名不够,fsearch 的另一个重头是文件内容索引化搜索。它的思路分两步:先用三元组索引(trigram index)加每文件过滤器把候选范围缩到最小,真正的读取只发生在"可能命中"的文件上。

这里有个容易被忽略的细节:匹配结果是现读的。README 明确说,命中内容从磁盘新鲜读取,上次同步之后编辑过的文件直接读原文。也就是说索引负责"指路",不负责"担保",你看到的永远是磁盘上的当前状态,不会有"索引说有这么一句、打开文件却找不到"的割裂感。

官方基准里有个数字很能说明这套设计的价值:一个刚编辑过的文件变得可被内容搜索到,从 2.1 秒降到了 1.3 毫秒;而 ripgrep 自己能搜的文件集合覆盖率,从 96% / 92% 提升到了 99.9% / 99.8%(同样是官方数据)。

三、增量更新:FSEvents 驱动,变更约 15 毫秒可见

全盘索引最怕两件事:首次建索引太慢,以及建完之后跟不上变化。fsearch 的做法是首次用 getattrlistbulk 完整爬一遍盘,之后接入 macOS 的 FSEvents 监听文件系统事件——创建、修改、删除都由系统推送,不需要应用自己轮询。按官方数据,变更大约 15 毫秒就能在索引里可见,改名或删除的可见时间是 18 / 15 毫秒;重启进程也不会推倒重来,只会重放变化的部分。

还有个很聪明的设计:首次运行时,你在某个文件夹里发起搜索,它会先索引这个文件夹。不用等全盘建完就能用,索引构建和日常使用完全不互相阻塞。

四、查询语法:一套过滤器,把"找文件"变成"问问题"

fsearch 的查询语法值得单独夸一夸。除了普通关键词,它有一整套过滤器:

fsearch 'readme in:~/Developer'          # 在指定文件夹内找
fsearch 'type:image size:>5mb mtime:<7d' # 大图,最近七天改过的
fsearch 'ext:rs regex:fn\s+\w+_dir'      # 在 rs 文件里按正则搜
fsearch 'sym:apply_dir'                  # 这个符号定义在哪
fsearch 'src/**/*.{c,h} !test/ init'     # glob,包含或排除目录
fsearch 'git:modified in:~/Developer/app' # 只看改过的文件

ext:、type:、kind:、in:、size:、mtime:、re:、path:、grep:、regex:、sym:、limit: 这些过滤器之外,还有一个对开发者极其友好的 git:——可以按 modified、staged、untracked、clean、ignored、conflict 等状态过滤。内容搜索默认智能大小写,正则按行匹配,行为和 ripgrep 一致,迁移成本几乎为零。

五、MCP 服务器:给 AI 编程助手装上"本地检索外挂"

2026 年这个时间点,一个工具不聊聊 AI 集成好像有点过意不去。fsearch 的做法很务实:fsearch mcp 启动一个跑在 stdio 上的 MCP 服务器,暴露 find_files、grep、multi_grep 三个工具。

这意味着你的编程助手(Claude Code、Cursor 这类支持本地 MCP 的客户端)可以直接问你机器上的文件:"apply_dir 这个函数定义在哪个文件?"不用再把路径复制来复制去,助手自己就有了一双能翻全盘的手。对习惯用 Agent 干活的开发者,这比再开一个搜索窗口顺手得多。

容易被忽略的设计取舍

工具的介绍文章通常只讲"能干什么",但我觉得一个工具真正的性格藏在它的取舍里。fsearch 有几个细节特别值得说说。

第一,内容索引是"刻意不全覆盖"的。 README 写明:内容搜索覆盖 1MB 以下的文本文件,git 仓库内放宽到 10MB(并且排除 git 忽略的文件);lockfile、100KB 以上的 JSON、压缩或打包产物、关联的 git worktree(主检出目录照样索引)、仓库外的构建输出目录,默认都不进内容索引。这是明摆着的权衡:用覆盖率换索引体积和查询速度。当然它也留了自定义的口子——在 ~/Library/Application Support/FSearch/content-skip.txt 里每行写一个文件夹就能追加排除。而且 README 补了一句很负责的话:文件名永远全量索引,你点名要 grep 的文件,就算在排除名单里也照样会读。

第二,权限不够时它选择静默跳过。 macOS 的 Full Disk Access(全磁盘访问)是刚需,不授权的话 Mail、Messages 等受保护目录默认读不到。fsearch 的处理方式是:没有权限,受保护的文件夹直接跳过,不弹窗、不报错、不中断。好处是流程丝滑,隐患是用户可能在不知情的情况下漏搜了一片目录,却以为搜的是全盘。另外 README 特别提醒:每次重建索引之后,需要去系统设置里重新授权一次。这种"把选择权交给你、但不唠叨"的风格,很Unix,也很考验用户自己的认知。

第三,一份索引,多进程共享。 同一台机器上第一个启动的 fsearch 进程是索引的 owner,后续启动的进程不会重复爬盘,而是共享这份索引——未来的图形应用和 CLI 也共用同一份。守护进程还跑在 malloc 的省内存模式下,空闲时会把内存还给系统。官方基准里守护进程常驻内存是 21 / 35MB,首次建索引的峰值内存 0.77 / 0.94GB,磁盘索引 309 / 600MB——同样都是官方数据。

第四,相同文件只索引一次,删掉的条目会被压缩清理。 内容索引里完全相同的文件只存一份,删除文件的条目会被 compact 掉。这决定了索引不会随时间无限膨胀,对"常驻好几年"的本地引擎来说,这是工程上的良心。

第五,作者把"优化的代价"也写进了 README。 官方基准里有一组对比很坦诚:新方案让磁盘索引小了三分之一、装的文本反而多了三分之一——办法是每个文件从两个过滤器减为一个 3-bit 过滤器、压缩 posting list 和路径、名字数组按位打包就地读取;但作者紧接着写明代价:grep 首页会多读几个文件,更深的拼写纠错在名字查询上会略慢一点。肯把收益和代价并排讲清楚的项目,比满纸"碾压""革命性"的宣传稿可信得多。

安装与使用

有两条路,任选其一。

路子一:从源码编译(需要 Rust 工具链):

cargo build --release && ./target/release/fsearch install   # 装到 ~/.local/bin/fsearch
fsearch fsearch main              # 按文件名找
fsearch 'ext:rs grep:apply_dir'   # 搜文件内容

路子二:直接下预编译包(仅 Apple Silicon,免 Rust 工具链):

curl -L https://github.com/noahdunnagan/fsearch/releases/latest/download/fsearch-aarch64-apple-darwin.tar.gz | tar xz && ./fsearch install

想让索引真正覆盖全盘,建议以登录项方式安装(fsearch install --login),然后在"系统设置 > 隐私与安全性"里给 ~/.local/bin/fsearch 授予 Full Disk Access 权限——记住,以后每次重建索引都要重新授权一次。

除了命令行,它还暴露了两层编程接口:一层是走 Unix socket(~/Library/Application Support/FSearch/fsearch.sock)或 fsearch stdio 的 JSON 行协议,支持 grep、open、recent 等操作;另一层就是 Rust crate,Engine::start 加 Query::parse 两行就能把引擎跑在你自己程序里。

JSON 接口里有个挺有人味的设计:open 操作用来告诉引擎"你打开了这个文件",它会在一段时间内把相关文件的排序提上去;near 参数则让离当前文件近的文件优先。翻译成人话就是——它记得你刚才在哪儿干活,并据此调整结果优先级。再加上 git: true 可以直接给每个命中标注 git 状态,recent 直接列出最近忙过的文件,这套接口与其说是搜索协议,不如说是一个懂开发者工作流的本地上下文服务。给 AI 用的话,fsearch mcp 一把梭。

适用人群

  • Mac 上文件以十万计起跳的开发者:大型仓库、多项目并行、目录套目录,全盘毫秒级检索是刚需,IDE 内置搜索覆盖不到机器上的其他角落;
  • 想给自己工具加搜索能力的人:Rust crate 加 JSON 接口,命令面板、文件管理器、编辑器插件不用从零实现索引和增量更新;
  • AI 编程助手重度用户:MCP 服务器让助手直接检索本地文件,Agent 干活少一道复制粘贴的工序;
  • 偶尔要做系统排查的人:找散落各处的配置文件、日志、某类后缀的文件分布,比手敲 find 加复杂参数省心得多。

反过来,如果你日常搜索需求用 Spotlight 或 IDE 内置搜索就能满足,或者你不在 macOS 上干活,那它对你就是零收益——它的高性能明确是围绕 macOS 设计的,引入之前先确认自己的场景。

写在最后

搜索这件事,本质上是在跟"数据越来越多、注意力越来越少"的现实对抗。fsearch 没有发明新算法,三元组索引、FSEvents、mmap 都是成熟技术,但它把这些东西扎实地拼成了一个开箱即用的本地引擎:0.1 毫秒的名字查询、容忍拼错的模糊匹配、跟得上手速的增量更新,再加上一个让 AI 助手也能使唤的 MCP 出口。

更难得的是它诚实——索引不覆盖哪些文件、权限不全会怎样、什么数据是官方自测,README 都写得明明白白。对一个要常驻你机器多年的工具来说,这种诚实比任何性能数字都值钱。

项目资源

注:文中所有性能数字均来自项目官方 README 的基准测试(M4 Mac mini,Linux / Chromium 源码树,中位数),为官方自测口径,实际表现随机器与数据规模而变化。