find 太慢?试试 fd:我实测扫描 12 万文件从 8.3 秒降到 0.9 秒
说实话,我以前一直觉得 find 挺好用的,直到有次在服务器上找一个日志文件。目录大概有 30 多万个文件,我敲了 find /var/log -name "*.log" -mtime -1,然后去倒了杯水,回来还在跑。等了快 20 秒才出结果。我当时就纳闷,这玩意儿不是号称 Unix 哲学典范吗?怎么这么慢。后来同事扔给我一个叫 fd 的小工具,说“你试试这个”。我一试,同样的目录,0.8 秒。真的,就 0.8 秒。从那天起,我的 find 使用频率直接掉了 80%。但我也不是无脑吹,后面会讲几个坑。
先说一下我的测试环境,免得有人说我瞎编。一台 2018 年的 Dell R730,双路 E5-2680 v4,64G 内存,系统是 Ubuntu 20.04.3 LTS,内核 5.4.0-91。文件系统是 ext4,挂载在 SSD 上。测试目录是 /usr,里面有 126,843 个文件(用 find /usr | wc -l 数的)。我跑了三次取平均值。find /usr -name "*.so" 平均 8.3 秒,fd -e so . /usr 平均 0.9 秒。差了 9 倍多。注意,fd 默认不搜索隐藏文件和目录,而 /usr 下本来就没有隐藏的,所以这个对比是公平的。如果你要搜隐藏文件,得加 -H,那速度会慢一点,但也就 1.2 秒左右。
为什么 fd 这么快?我翻了一下它的源码和文档。fd 是用 Rust 写的,底层用了 regex 和 ignore 两个 crate。ignore 这个库是 ripgrep 的作者写的,专门做并行目录遍历,会自动读取 .gitignore 并跳过。而 find 是 C 写的,GNU find 的实现很古老,单线程递归,每次系统调用都老老实实。当然,find 也有它的优势,比如 POSIX 兼容,几乎任何 Linux 都有,脚本里写 find 不用担心中间件没装。我有次写了个部署脚本,用了 fd,结果在一台 CentOS 6 的机器上直接报 command not found,当时就尴尬了。所以现在我的原则是:交互式搜索用 fd,写脚本还是 find,或者用 command -v fd 判断一下。
如果你也想试试 fd,安装很简单。Ubuntu/Debian:sudo apt install fd-find,注意装完命令叫 fdfind,因为和另一个叫 fd 的包冲突。你可以 ln -s $(which fdfind) ~/.local/bin/fd 建个软链接。CentOS/RHEL 8+:sudo dnf install fd-find。Arch:sudo pacman -S fd。macOS:brew install fd。然后几个常用参数:fd -e jpg 找 jpg 文件;fd -x rm 删除找到的文件(危险,别乱用);fd -H 包含隐藏文件;fd -I 不忽略 .gitignore;fd -j 8 指定并行线程数,默认是 CPU 核心数。还有一个我特别喜欢的:fd pattern -x wc -l 可以并行统计每个文件的行数,比 find -exec 快得多,因为 find -exec 是串行的。
最后说点个人看法。工具这东西,没有银弹。fd 快,但默认行为跟 find 不一样,比如它默认忽略 .git 和 .gitignore 里的文件,这在你搜代码库的时候很爽,但如果你要搜一个被 git 忽略的日志文件,就会漏掉。我有次找 .env 文件,fd .env 死活搜不到,后来才想起来 .env 在 .gitignore 里。得加 -I 才行。还有,fd 的正则语法是 Rust 的 regex,跟 find 的 -regex 不太一样,迁移的时候要小心。所以我的建议是:花 10 分钟学一下 fd,日常搜索能省不少时间,但别把它当成 find 的完全替代品。至少,find 的 -execdir 和 -ok 这些安全特性,fd 目前还没有对应功能。就这样。