上一篇写完 ripgrep 之后预告过,这次实测 sharkdp/fd(读星数约 4.5 万,2026-11-04)。
找文件这件事,很多人还在靠 find . -name '*.py' -type f 硬敲。参数一长就容易写反:-name 和 -iname 分不清,排除 node_modules 要手写一串 ! -path,权限不够时 stderr 刷屏,脚本里还得统一 2>/dev/null。fd 被宣传成「更好用的 find」:命令短、默认忽略 .gitignore 和隐藏文件、结果安静。到底快多少、默认行为省了什么、哪些坑会让人以为「fd 漏搜了」,我在本机跟 GNU find 正面比了一轮。
本机实测 + AI 辅助整理。测量时间:2026-11-04(Asia/Shanghai)。每组先预热 1 次,再取 5 次(
/usr那组取 3 次)的中位数,文件都在页缓存里,属于「热缓存」成绩。
本机环境
OS: Debian 13 x86_64,8 核
fd: 10.3.0(GitHub release 二进制)
find: GNU findutils 4.10.0
安装说明要先泼一盆冷水:Debian/Ubuntu 的包名是 fd-find,装完命令往往是 fdfind(跟另一个叫 fd 的工具撞名)。本机当天 apt 源直接 500,没装上 deb 包,改用官方 release:
# 官方二进制(本机实际路径)
curl -sL -o fd.tar.gz \
https://github.com/sharkdp/fd/releases/download/v10.3.0/fd-v10.3.0-x86_64-unknown-linux-gnu.tar.gz
tar xzf fd.tar.gz
cp fd-v10.3.0-x86_64-unknown-linux-gnu/fd ~/.local/bin/fd
# 若你装的是 apt 包,通常是:
# sudo apt install fd-find
# ln -s $(which fdfind) ~/.local/bin/fd
macOS 一般是 brew install fd,Windows 可用 winget / scoop。下文统一写 fd。
测试素材
复用上一篇 ripgrep 的同一套目录(方便对照):
src/:Python 标准库 + dist-packages,约 98M、4204 个文件node_modules/:前端依赖,约 66M、1081 个文件- 整包约 164M、5917 个条目;根目录
git init,.gitignore里写了node_modules/
另外拿整个 /usr(3.6G、约 6.2 万条目) 做大目录测试。
实测 1:164M 项目目录——find 经常更快
先让两边搜的范围尽量一致:fd -u 表示不看 ignore、也尽量放开限制,跟 find 全扫对齐。
| 场景 | find 中位数 | fd -u 中位数 | 结果数 | 谁快 |
|---|---|---|---|---|
-name '*.py' / -e py | 0.010 s | 0.026 s | 1797 = 1797 | find |
-name '*.md' / -e md | 0.014 s | 0.023 s | 26 = 26 | find |
-name '*.js' / -e js | 0.009 s | 0.024 s | 962 = 962 | find |
-iname '*.PY' / -i -e PY | 0.009 s | 0.016 s | 1797 = 1797 | find |
-type f -name '*.json' / -t f -e json | 0.009 s | 0.023 s | 17 = 17 | find |
-maxdepth 2 -name '*.py' / -d 2 -e py | 0.007 s | 0.016 s | 0 = 0 | find |
热缓存、目录也就一百多兆时,GNU find 反而更轻。fd 有启动开销和并行调度,这种体量上体现不出来。网上截图里「快几十倍」的场面,在这个 corpus 上我复现不出来——这点必须先说清楚。
实测 2:默认模式才是卖点——962 个 js 只剩 1 个
不加 -u,直接 fd -e js:
find . -name '*.js' 0.009 s 962 个
fd -u -e js 0.024 s 962 个
fd -e js(默认,读 .gitignore) 0.027 s 1 个
那 961 个都在 node_modules/ 里。.md 更极端:26 个全在依赖目录,fd -e md 直接 0 个;find 会全部吐出来。
等价的 find 要自己写排除:
find . -name '*.js' ! -path './node_modules/*' ! -path './.git/*'
# 结果:1 个,和 fd 默认一致
日常在项目里找文件,这才是 fd 最大的好处:不用每次记得排除依赖目录,命令也短得多。速度不是主角,噪音过滤才是。
实测 3:/usr 3.6G——大约 1.5 倍,不是数量级碾压
| 命令 | 中位数 | 结果数 |
|---|---|---|
find /usr -name '*.so' | 0.072 s | 954 |
fd -u -e so . /usr | 0.046 s | 954 |
fd -e so . /usr(默认) | 0.056 s | 954 |
find /usr -name '*.conf' | 0.071 s | 296 |
fd -u -e conf . /usr | 0.038 s | 296 |
find /usr -type f -name '*.py' | 0.070 s | 5418 |
fd -u -t f -e py . /usr | 0.048 s | 5418 |
*.so 这一组,find 72ms 对 fd 46ms,大约 1.6 倍。把 fd 压成单线程:
fd -u -j1 -e so . /usr 中位数 0.078 s
fd -u -j8 -e so . /usr 中位数 0.042 s
单线程几乎和 find 打平,多线程才拉开差距。浅层搜索(-maxdepth 3 找 *.h)find 反而更快(0.006 s vs 0.019 s)——树不深时并行没意义。
结论:大目录上 fd 有优势,但别拿「几十倍」去说服同事;本机热缓存成绩大概就是 1.5~2 倍量级。
注意命令写法:fd -e so /usr 是错的——/usr 会被当成搜索「模式」而不是路径,直接报错、结果 0。正确是 fd -e so . /usr 或 fd -e so --search-path /usr。这个坑下文单独写。
实测 4:类型、深度、大小写——参数短,语义清楚
在踩坑用的小目录 b/(已 git init)里核对这几个开关:
fd -t f -e py . b → b/main.py, b/src/app.py, b/src/token_helper.py
fd -t d . b → b/src/
fd -d 1 -e py . b → 只有 b/main.py
fd -d 2 -e py . b → 含 src 下两个 .py
fd -i TOKEN . b → 命中 token_helper.py(默认已跳过 build/)
对照 find:-type f / -type d / -maxdepth / -iname 都能做同样的事,但拼起来更长,也更容易把路径写进错误位置。fd 把高频组合收成短旗标,这是体验分,不是跑分。
踩坑清单(都在本机复现过)
坑 1:Debian 包名是 fd-find,命令常是 fdfind
本机 apt-get install fd-find 因镜像 500 失败,改走 GitHub release。但发行版包装逻辑仍然要知道:包叫 fd-find,二进制经常是 fdfind。装完敲 fd 找不到命令,先 which fdfind,再 ln -s 或 alias fd=fdfind。
坑 2:默认不搜隐藏文件——.env 直接消失
$ fd env a
(空)
$ fd --hidden env a
a/.env
$ find a -iname '*env*'
a/.env
文件名带 token 的隐藏文件同理:fd token 看不到 .secret_token,要加 --hidden;连 ignore 一起关掉用 -u --hidden。
坑 3:.gitignore 有时生效、有时不生效
同样的目录、同样的 .gitignore(里面写着 build/):
目录 a(没有 git init): fd token → 仍包含 a/build/token_cache.json
目录 b(git init 过): fd token → 只剩 b/src/token_helper.py
目录 a 加 --no-require-git: build/ 被跳过
fd 默认只在 git 仓库里才认 .gitignore(和 ripgrep 那篇的坑非常像)。刚解压的源码包、没 init 的文件夹,ignore 等于没写。需要时加 --no-require-git,或用 fd 自己的 .ignore / .fdignore。
坑 4:*.py 是正则不是 glob;路径别当成模式
$ fd '*.py' a
[fd error]: regex parse error:
*.py
^
error: repetition operator missing expression
fd 默认模式是正则,* 是量词。按 glob 找扩展名用 fd -g '*.py',或更干脆 fd -e py。
更隐蔽的一个:
$ fd -e so /usr
[fd error]: The search pattern '/usr' contains a path-separation character...
参数顺序是 fd [选项] [模式] [路径...]。写成 fd -e so /usr 时,/usr 变成了模式。本机正确写法:
fd -e so . /usr # 模式 '.',路径 /usr
fd -e so --search-path /usr
坑 5:权限拒绝时,find 刷屏,fd 安静
构造一个不可读目录后:
$ find perm -name '*.txt'
perm/visible/found_me.txt
find: 'perm/locked': Permission denied
$ fd -e txt . perm
perm/visible/found_me.txt
find 往 stderr 吐了 43 字节报错;fd 的 stderr 是 0 字节,结果里照样只有有权限的文件。脚本里抓 find 输出时经常要 2>/dev/null,fd 省这一步——也意味着你可能根本不知道有目录没扫到。
顺手好用的几个参数
fd -e py 'token' # 扩展名 + 文件名正则
fd -t f -e json config # 只找普通文件
fd -t d # 只找目录
fd -d 2 -e py # 最大深度 2
fd -i TOKEN # 忽略大小写
fd --hidden -u 'env' # 隐藏文件 + 不看 ignore
fd -j1 ... # 单线程(对比/调试用)
小结
| 项 | 本机结果 |
|---|---|
| 164M 项目目录(同范围) | find 经常更快(约 7~14ms vs 16~27ms) |
| 默认模式 | 自动跳过 .gitignore:*.js 962→1,*.md 26→0 |
3.6G /usr | 约 1.5~1.9 倍;单线程与 find 接近 |
| 浅层 maxdepth | find 仍可能更快 |
| 坑 | fdfind 包名、隐藏文件、非 git 的 ignore、正则/路径当模式、权限错误安静吞掉 |
一句话:找文件请优先想「我要不要依赖目录和隐藏文件」;要干净结果用 fd 默认模式,要比毫秒级极限速度在小目录上 find 并不差。 它跟 ripgrep 是同一思路的「友好默认」,也是同一类双刃剑——省心的开关,查密钥、查构建产物、查没 git init 的解压包时,记得主动加 --hidden / -u / --no-require-git。
完整的测量脚本和原始日志在本机 fd/ 目录留档;整理版以后会放到 Gumroad,先关注专栏「GitHub实测笔记」。
上一篇:ripgrep 本机实测(3.6G 目录 grep vs rg,附「62 倍有水分」)。
下一篇预告:sharkdp/bat——带语法高亮的 cat,本机对比 cat/less 的启动与大文件体验。有问题评论区见,看到都会回。