fd 本机实测:/usr 找 *.so,find 72ms、fd 46ms;小目录反而 find 更快

0 阅读4分钟

上一篇写完 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 py0.010 s0.026 s1797 = 1797find
-name '*.md' / -e md0.014 s0.023 s26 = 26find
-name '*.js' / -e js0.009 s0.024 s962 = 962find
-iname '*.PY' / -i -e PY0.009 s0.016 s1797 = 1797find
-type f -name '*.json' / -t f -e json0.009 s0.023 s17 = 17find
-maxdepth 2 -name '*.py' / -d 2 -e py0.007 s0.016 s0 = 0find

热缓存、目录也就一百多兆时,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 s954
fd -u -e so . /usr0.046 s954
fd -e so . /usr(默认)0.056 s954
find /usr -name '*.conf'0.071 s296
fd -u -e conf . /usr0.038 s296
find /usr -type f -name '*.py'0.070 s5418
fd -u -t f -e py . /usr0.048 s5418

*.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 接近
浅层 maxdepthfind 仍可能更快
坑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 的启动与大文件体验。有问题评论区见,看到都会回。