为什么 AI harness 都爱用 rg ?
用久了 AI cli 发现它们大都在检索文件/代码时使用
rg
于是自己去翻了一下 rg 仓库, 并把它与grep做了下对比
TL; DR
rg 的优势在于场景下的 搜索速度 和 准确度。它对于git仓库有 特性优化,对 coding agent 来说,少搜无关内容、少产生噪声,往往比单纯快几毫秒更有价值。
正文
rg 全名 ripgrep
命名有点意思, 让 grep 直接下岗(R.I.P.)吗? 😂
那么,rg 和 grep 有什么区别,为什么看 coding agent 干活,调的全是 rg?
默认行为:搜哪些文件
我在本机准备了一个只有几个文件的小仓库:src/a.rs、src/b.py、build/out.js 和 .hidden/note.txt。.gitignore 排除了 build/ 和 .hidden/,文件里都包含 TODO。
在仓库根目录执行 rg TODO,默认只会显示未被忽略的代码文件,例如:
src/a.rs:fn main() { /* TODO: fix */ println!("hello TODO"); }
而 grep -r TODO . 会继续进入 build/、.hidden/,甚至 .git/,把里面的匹配也列出来。grep 并不知道 .gitignore 代表什么;它只是递归搜索指定目录。
若想让 grep 在这个仓库里避开常见的生成目录和依赖目录,就得自己补上排除规则,例如:
grep -rn TODO . \
--exclude-dir=.git --exclude-dir=node_modules --exclude-dir=build \
--exclude-dir=dist --exclude-dir=.venv --exclude-dir=__pycache__ \
--exclude='*.min.js' --exclude='*.map'
仓库越复杂,这些规则就越容易变成长长的一串。rg 默认遵循 .gitignore,也会跳过隐藏文件和二进制文件;这让它在代码仓库里的默认搜索范围通常更接近开发者的预期。
另外,两者还有两个常见差异:
- 大小写:两者默认都区分大小写。
rg -S启用智能大小写:模式全小写时忽略大小写,含大写时仍严格匹配。例如rg -S todo可以匹配Todo、todo和TODO。 - 行号:
rg在终端输出时通常显示行号;输出被管道接走时,默认可能省略行号。需要稳定输出行号时,显式加-n。grep需要通过-n请求行号。
性能:快多少,差在哪里
我用两类仓库做了对比。每次先预热,再运行三次并取最短时间。环境是 arm64、2 核,ripgrep 14.1.1 和 GNU grep 3.11。以下数字是本机结果,不代表所有机器或仓库。
干净仓库:Django,7087 个文件,约 38 MB 源码
| 查询 | rg | grep | rg 用时优势 |
|---|---|---|---|
字面量 TODO | 0.063s | 0.116s | 1.8 倍 |
正则 def\s+\w+\(self | 0.091s | 0.197s | 2.2 倍 |
-w save(整词) | 0.070s | 0.158s | 2.3 倍 |
| 连续搜索 20 次 | 1.55s | 2.91s | 1.9 倍 |
在这组数据里,两者搜索的文件范围接近,rg 大约快两倍。差距主要反映了实现和并行策略等因素,不能简单归因于单一原因。
包含 node_modules 的本机仓库,约 5 万个文件
| 指标 | rg | grep -r |
|---|---|---|
| 耗时 | 0.094s | 1.735s(约 18.4 倍) |
| 结果行数 | 207 | 2504 |
| 输出字节数 | 25,626 | 17,375,526 |
| 命中文件位置 | 全部在源码目录 | 64% 在 node_modules |
这组结果里,决定体验的首先是搜索范围:grep 搜出了大量依赖代码,输出约 17 MB;rg 的输出只有约 25 KB。对 agent 来说,搜索结果还要进入上下文窗口,输出太多会被截断,也会挤掉真正有用的信息。
同时,我也测了一个反例:列文件时,rg --files 用时 0.043s,find . -type f 用时 0.036s,find 略快。rg 并不是所有任务都更快,它的优势集中在代码仓库搜索及其默认过滤行为上。
需要留意,这台机器只有 2 核,而 rg 默认会并行搜索;不同硬件和数据集会改变结果。对于单个大文件,或使用 C locale 的简单搜索,grep 也可能表现得很好。
搜索引擎和表达式语法
rg 默认使用 Rust 的正则表达式引擎。它通过字面量优化、有限自动机和 SIMD 等技术提高搜索效率;目录遍历则由 ignore 等组件处理,并可以并行执行。
有限自动机也带来一个取舍:默认引擎可以保证正则搜索的线性时间,但不支持反向引用和环视等需要回溯的语法。比如:
$ rg '(a)\1' br.txt
rg: regex parse error:
(?:(a)\1)
^^ error: backreferences are not supported
Consider enabling PCRE2 with the --pcre2 flag
遇到这类语法,可以用 -P 切换到 PCRE2,例如 rg -P 'a(?=b)'。
GNU grep 提供 BRE、ERE 和 PCRE 三种模式,分别通过 -G、-E 和 -P 选择。不同模式的转义规则不完全相同:例如反向引用在 BRE 中写作 \(a\)\1,GNU grep 的 ERE 扩展中则可写作 (a)\1。脚本的匹配和性能也可能受 locale 影响;设置 LC_ALL=C 与使用 UTF-8 locale 时,表现可能不同。
rg 的默认语法和 Unicode 行为则更一致。例如 rg -w nai 不会把 naïve 中的 naï 当成一个完整单词来匹配。若需要 grep 的 PCRE 能力,rg -P 仍然可用。
所以,为什么 agent 常用 rg
速度是一部分原因,默认搜索范围和输出形式同样重要。
首先,主流 coding agent 往往会随产品提供 rg,或把它列为依赖。其次,一次代码任务通常要反复查定义、调用点、测试和配置;忽略文件和依赖目录能减少重复搜索产生的噪声。
输出也比较适合工具消费。rg 会按需输出文件路径和行号,--vimgrep 可包含列号,--json 则提供结构化结果,其中有匹配行、行号、偏移量和子匹配信息。相比让模型从大量无关文本里筛选,规整、聚焦的结果更容易继续处理。
.gitignore 还有一层实际意义:它反映了项目维护者认为哪些内容不属于日常源码搜索范围。rg 默认沿用这套约定,agent 因而不必每次重新遍历依赖、构建产物和缓存目录。
什么时候用 grep
grep 仍然有它更合适的场景:
- 可移植性:POSIX 环境提供
grep。最小化 CI 镜像、BusyBox 和老脚本通常可以直接使用它;rg不承诺 POSIX 兼容。 - 不能安装额外工具的机器:生产环境或受限容器里,
grep可能已经是唯一可用的选择。 - 依赖既有 BRE/ERE 行为的脚本:保留原命令通常比迁移表达式更省事。
- 管道过滤:例如
cat log | grep -v DEBUG。rg也能读标准输入,但这里用grep简单直接。 - 单文件、纯文本、简单模式:这时递归遍历和 ignore 规则都派不上用场,两者差距通常不大。
反过来,也要记得 rg 的默认过滤可能让某些文件“消失”。要搜被忽略的文件,可用 --no-ignore;要纳入隐藏文件,可用 --hidden。rg -uuu 会同时关闭 ignore、隐藏文件和二进制过滤,搜索范围会更接近 grep -r。因此,遇到“明明文件在,却搜不到”时,先检查这些默认规则。
常用命令对照
| 目的 | grep | rg |
|---|---|---|
| 递归搜索 | grep -rn pat . | rg -n pat |
| 只列文件名 | grep -rl pat . | rg -l pat |
| 计数 | grep -rc pat . | rg -c pat |
| 忽略大小写 / 智能大小写 | grep -i | rg -i / rg -S |
| 整词 / 固定字符串 | grep -w / grep -F | rg -w / rg -F |
| 只搜某种语言 | --include='*.py' | rg -t py(排除用 -T py) |
| 排除文件 | --exclude='*.min.js' | rg -g '!*.min.js' |
| 排除目录 | --exclude-dir=dist | rg -g '!dist/' |
| 显示上下文 | -A/-B/-C | -A/-B/-C |
| 多行匹配 | grep -Pzo(输出处理较麻烦) | rg -U |
| 显示匹配位置 | 无直接对应选项 | rg --vimgrep / rg --json |
| 替换匹配文本 | 配合 sed 等工具 | rg --replace(只改输出,不修改文件) |
| 搜压缩文件 | 通常先解压 | rg -z |
| 指定文本编码 | 通常先转码 | rg -E gbk |
| 搜索统计 | 无直接对应选项 | rg --stats |
| 列出文件 | find . -type f | rg --files |
| 反向引用 / 环视 | grep -P 或 BRE/ERE 方言 | rg -P |
rg 也支持通过 RIPGREP_CONFIG_PATH 指定配置文件,把常用选项固定下来。GNU grep 曾经支持的 GREP_OPTIONS 已被废弃,不建议依赖它。
自己动手复现
文中的数据来自本机。你可以先确认版本和搜索范围,再按相同条件计时:
rg --version
rg --stats -n 'PATTERN' .
rg --files | wc -l
find . -type f | wc -l
# 先预热,再重复运行并比较多次结果
time rg -n --no-heading 'TODO' .
time grep -rn 'TODO' .
最后回到开头的问题:装 rg 不一定会让每一次搜索都快十几倍。它在代码仓库里的价值,是默认把搜索范围收在更有用的地方。速度是加分项,少看无关结果才是 agent 最直接的收益。
参考:ripgrep README 与 FAQ、GNU grep 手册,以及互联网上 Coding Agent 的相关信息。
更多推荐


所有评论(0)