ripgrep + fd + bat 现代命令行三件套实战:替换 grep/find/cat,服务器日志与代码检索提速全流程

为什么你的 grep 越用越慢

每个常年和服务器打交道的人,都有过这样的经历:线上出了故障,要在一个几 GB 的 Nginx access.log 或者几十万行的代码仓库里找一条线索,敲下 grep -r "关键词" .,然后盯着屏幕等上半分钟。更糟的是,一旦目录里混进了日志文件、二进制文件或者 node_modules,grep 会把它们全扫一遍,结果又慢又乱。

这并不是你的机器不行,而是 grep、find、cat 这三个命令都诞生于上个世纪,它们的默认行为是为"正确"服务的,而不是为"快"和"好用"服务的。过去十年里,Rust 生态里冒出了一批专门用来替代它们的新工具,核心思路只有两个:默认忽略你不关心的东西,以及默认并行。这篇文章把最实用的三件套——ripgrep、fd、bat——讲清楚,让你以后在服务器上找东西的时间至少砍掉一半。

ripgrep:替代 grep,默认快十倍

安装

Debian / Ubuntu 直接装:

apt update
apt install -y ripgrep   # 命令名是 rg
rg --version

CentOS / Rocky 走 EPEL:

yum install -y epel-release
yum install -y ripgrep

如果系统源太旧,直接从 GitHub 下静态二进制,扔进 /usr/local/bin 即可,没有任何依赖。

它为什么快

ripgrep(命令名 rg)基于 Rust 的 regex 引擎,自带多线程,底层用的是内存映射加 SIMD 扫描。官方基准里,在一个 Linux 内核源码树上搜一个词,rg 通常比 grep -r 快 3 到 10 倍。更重要的是,它默认就会跳过 .gitignore 里列出的文件和目录,也会跳过隐藏文件和二进制文件。这意味着你在项目目录里搜东西,不会再被 .git、node_modules、vendor 这些噪音淹没。

最常用的几个姿势

# 递归搜当前目录,默认智能忽略
rg "upstream_timeout"

# 只搜 php 文件
rg -t php "mysqli_connect"

# 列出文件类型简写
rg --type-list | head

# 显示行号(默认已开)、显示列号
rg --column "session_start"

# 搜不可忽略的目录(比如全部文件,含隐藏)
rg -uu "关键词"

# 只列出匹配的文件名,不打印内容(配合管道超有用)
rg -l "TODO" | xargs sed -n '1p'

# 统计每个文件匹配了多少行
rg -c "error" /var/log/nginx/

实战:在 Nginx 日志里找可疑 IP

假设网站被刷了,要找出访问量最高的前 20 个 IP。传统写法是 awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20,但当日志有几十 GB 时,瓶颈其实在读文件。用 rg 先做一遍筛选再交给 awk,可以只处理你关心的那部分:

# 只要 5xx 的请求,再统计 IP
rg " 5[0-9][0-9] " access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

# 找出访问了某个路径最多的人
rg "\"GET /api/login" access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head

这里的关键是 rg 的过滤足够快,后面的 awk 才有意义。如果先 awk 再过滤,等于把整个文件都解析了一遍。

几个必须知道的坑

  • rg 默认不搜隐藏文件(以 . 开头的)。要搜 .env、.htaccess 得加 --hidden,再配合 --no-ignore 才能无视 gitignore。
  • 搜二进制文件会提示 binary file matches,用 -a 强制当文本处理。
  • 正则语法默认是 Rust 风格,和 PCRE 略有差别。复杂正则可以加 -P 切到 PCRE2 引擎。
  • 替换文件内容不建议用 rg 做(它没有原地替换),要批量替换用 sd 或者传统的 sed -i。

fd:替代 find,语法短到不用查手册

find 的强大毋庸置疑,但它的语法是出了名的反人类:想找当前目录下所有扩展名是 .log 且修改时间在一天内的文件,你得写 find . -name "*.log" -mtime -1,而想排除某个目录又要加一长串 -not -path。fd 把这件事变得像说话一样。

安装

apt install -y fd-find    # 注意:Debian 上命令名叫 fdfind
# 或者建立软链
ln -s $(which fdfind) /usr/local/bin/fd

Rust 官方包和 Homebrew 装出来的命令名就是 fd,只有 Debian/Ubuntu 为了避让一个同名老包改叫 fdfind,这是新手最容易懵的一点。

对照表:find 怎么写,fd 怎么写

需求findfd
找所有 .log 文件find . -name "*.log"fd -e log
找名字含 config 的文件find . -name "*config*"fd config
排除 node_modulesfind . -name "*.js" -not -path "*/node_modules/*"fd -e js -E node_modules
只找目录find . -type d -name "cache"fd -t d cache
只找可执行文件find . -type f -perm -u+xfd -t x
一天内改过的文件find . -mtime -1fd --changed-within 1d

看最后一行的差距:fd --changed-within 1d 可以直接写 2h、30min,find 的 -mtime 只认天数,要精确到小时还得用 -mmin 自己换算。

实战:找大文件、批量清理

# 找出所有超过 100M 的日志文件
fd -e log --size +100M

# 找出所有 .tmp 文件并删除(先打印确认,再执行)
fd -e tmp -x rm -v

# 找到所有 .php 文件,对每一个执行语法检查
fd -e php -x php -l

# 找所有空的目录
fd -t d --size -1M --max-depth 3 -x rmdir -v

-x 后面的命令会对每个结果执行一次,{} 代表文件名占位符,如果不写就自动追加到末尾。-X(大写)则是把所有结果一次性拼成一条命令执行,适合 tar、rm 这种可以一次吃多个参数的场景。

bat:替代 cat,带高亮还会自动分页

cat 的问题在于它把文件原样吐出来,没有行号、没有语法高亮、没有分页,看一个 500 行的配置文件要来回滚。bat 把这三件事都做了,而且在输出被重定向到管道时,会自动退化成纯文本,不会污染你的脚本。

安装

apt install -y bat
# Debian 上新版叫 batcat,同样建软链
ln -s $(which batcat) /usr/local/bin/bat

常用场景

# 看一个配置文件(自带行号 + 高亮 + 分页)
bat /etc/nginx/nginx.conf

# 只显示某几行
bat -r 20:40 /etc/nginx/nginx.conf

# 去掉行号和装饰,纯粹为高亮
bat -pp /etc/mysql/my.cnf

# 看非文本文件时用 -A 显示不可见字符
bat -A /tmp/weird.txt

# 和 rg 联动:搜索并高亮上下文
rg -n "worker_connections" /etc/nginx/ | bat

bat 默认会调用 less 分页,如果输出被重定向到文件或管道,它会自动改为直接输出,这一点比很多人手动写 cat | less -R 省事得多。

让 bat 接管 man(可选但很香)

export MANPAGER="sh -c 'col -bx | bat -l man -p'"
# 加进 ~/.bashrc 后,man ls 会变成彩色带行号的页面

把三件套串成一条工作流

单独用每个工具已经能提升效率,真正拉开差距的是把它们和管道串起来。下面是一个"线上故障排查"的典型组合,假设你要在一个老项目的代码里找是谁把数据库连接写死了:

# 1. 先用 fd 找出所有可能的配置文件
fd -e php -e env -e ini -e yml . config/ src/

# 2. 用 rg 搜可疑的关键字,只看文件名
rg -l "max_connections|persistent" --type-add 'conf:*.{ini,yml,env}' -t conf

# 3. 用 bat 带着上下文看这几个文件
bat -r 1:60 $(rg -l "max_connections" | head -1)

再比如每天早上做一次日志巡检:

# 找出昨天的错误行,统计最常见的报错
rg " 500 " /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head

# 所有服务器上,改过一天的配置文件的清单(配合 fmt 输出更好读)
fd -e conf --changed-within 1d -x stat -c '%n %y'

性能对比与选型建议

场景推荐工具理由
大仓库全量搜索rg并行 + 智能忽略,最快
需要复杂 Perl 正则rg -P 或 agPCRE2 兼容性更好
按名字/时间找文件fd语法短,支持排除
需要极复杂的条件组合find功能最全,脚本里更通用
看配置文件bat高亮 + 行号
脚本里纯文本读取cat / head无依赖,避免意外

需要强调的是:这三个新工具不是要你删掉老的。在写脚本、做无人值守任务、给别人的机器上用的时候,grep、find、cat 依然是最稳妥的选择,因为每台机器都有。它们的价值在于你手动操作、临时排查的那些时刻——那才是真正消耗时间的地方。我的建议是先在本地和跳板机上把别名配好,用顺了再考虑在服务器上也装上。

常见问题排查

1. rg 明明有这个词,却搜不到

最常见的原因是被 gitignore 忽略了,或者文件是隐藏文件。先加 rg -uu 试试;如果还是搜不到,用 rg --debug 看它到底扫了哪些文件,或者确认该文件是不是被当作二进制跳过了(加 -a)。

2. fd / bat 命令找不到

Debian / Ubuntu 下它们分别叫 fdfind 和 batcat,建个软链就行。别去改 PATH 折腾。

3. bat 在服务器上分页导致脚本卡住

这是把 bat 用在了非交互场景。记住:bat 检测到输出不是终端时会自动关掉分页,但如果你显式加了 --paging=always 就会强制分页。脚本里永远不要用 bat,用 cat。

4. 装了 ripgrep 但 rg 报 command not found

用静态二进制安装时忘了加执行权限,或者没放进 PATH。检查 which rg 和 ls -l /usr/local/bin/rg。

5. 搜索结果太多,怎么只保留最相关

用 rg -c 先看每个文件的命中数,或者 rg --sort path 稳定排序,再配合 head 只看前几条。找线索时不要一次看全部,要分层收敛。

小结

ripgrep、fd、bat 这三个工具加起来不到 20MB,却能在每一次手动排查中替你省下几分钟。它们的共同哲学是:不追求功能上的完备,而是把 90% 的日常场景做到极致的快和顺手。对个人站长和运维来说,真正稀缺的是注意力,用更好的工具把重复劳动压缩掉,才能把时间留给真正需要思考的问题,比如架构、性能和内容。建议今天就在你的常用服务器上装一遍,把三条别名写进 .bashrc,用一周你就会回不去了。

Last modification:October 15th, 2026 at 08:26 pm

Leave a Comment