ripgrep 实战:比 grep 快 10 倍的代码搜索

ripgrep(命令行 rg)是一款用 Rust 编写的递归代码搜索工具。它默认遵守 .gitignore、自动跳过隐藏文件与二进制文件,搜索速度常常比传统 grep 快一个数量级。对于每天要在大型代码库里翻找符号、TODO、配置项的开发者来说,换上 ripgrep 几乎是无成本的性能升级。

一个常见的场景是:你在仓库存放了一个 50 万行的单体服务,依赖目录 node_modules 里还有几万个文件。用 grep -r "TimeoutException" 会先把 node_modules 翻个底朝天,屏幕刷满噪音、十几秒才出结果;而 rg "TimeoutException" 往往在 1 秒内给出干净的答案。差别不是“快一点”,而是“能不能忍着用它”。

一、为什么需要 ripgrep

很多团队还在用 grep -r 满仓库搜,但 grep 有几个天然短板:它不会主动忽略 .gitignore 里声明的文件,也不会跳过 node_modules 这类巨型目录,结果既慢又产生大量噪音。ripgrep 的设计哲学恰恰相反——“默认就做对的事”。

工具默认忽略 .gitignore默认跳过二进制并行遍历语言核心亮点
grepC系统自带、无需安装
ackPerl专为代码优化的正则
agCripgrep 的前身思路
ripgrepRust最快、零配置、安全正则引擎

ripgrep 之所以快,关键在于三点:并行递归目录遍历、Rust regex 引擎(基于有限自动机,不会 catastrophic backtracking)、以及利用 SIMD 指令加速字面量匹配。

二、安装:一条命令搞定

几乎所有包管理器都已收录 ripgrep,按你的平台选一条:

# macOS
brew install ripgrep

# Ubuntu 22.04+ / Debian
sudo apt install ripgrep

# Arch
sudo pacman -S ripgrep

# Windows (scoop)
scoop install ripgrep

# 通用:Rust 工具链
cargo install ripgrep

# 或直接从 GitHub Releases 下载预编译二进制
# https://github.com/BurntSushi/ripgrep/releases

装好后用 rg --version 验证,再跑 rg --type-list 可以看到它原生支持的两百多种文件类型(py、js、go、rs、md……)。

三、最常用命令:10 秒上手

ripgrep 的默认行为已经足够好,下面这几条覆盖了 80% 的日常场景:

# 递归搜索 "fn handle"(默认忽略 gitignore / 二进制 / 隐藏文件)
rg "fn handle"

# 不区分大小写
rg -i "TODO"

# 显示行号(默认已显示;-h 仅输出内容不含文件名)
rg -n "error" src/

# 只列出命中文件(配合 fzf 做二次过滤非常爽)
rg -l "deprecated"

# 统计每个文件的匹配数
rg -c "panic"

# 只看命中文件总数
rg -l "unwrap" | wc -l

如果你已经在用 fzf 模糊查找 重塑终端工作流,rg -l 输出的文件列表直接管道给 fzf,就能瞬间得到一个可交互的“搜索结果浏览器”。

四、按文件类型与目录过滤

ripgrep 的类型系统让“只在 Python 文件里搜”变成一行命令,不必手写复杂 glob:

# 只在 Python 文件里搜
rg -t py "import os"

# 排除某种类型(例如跳过测试)
rg -T test "connect"

# 自定义 glob:只看 src 下、排除 min.js
rg -g 'src/**' -g '!*.min.js' "fetch"

# 强制搜索隐藏文件(默认跳过 .git、.env 等)
rg --hidden "api_key"

# 连 gitignore 忽略的文件也搜(慎用,会慢)
rg -u "secret"        # -u 搜隐藏;-uu 搜忽略;-uuu 搜全部含二进制

五、替换与结构化输出

5.1 用 -r 做整库重命名式替换预览

ripgrep 本身不写文件(那是 sed 的活),但 -r 可以输出“替换后”的内容,方便你先确认再落地:

# 把 foo 改成 bar,只预览替换结果(不修改文件)
rg -r "bar" "foo" --no-heading

# 配合 sed -i 真正落地(先 rg 确认范围,再 sed 执行)
rg -l "old_api" | xargs sed -i '' 's/old_api/new_api/g'

5.2 用 –json 接入 jq 做自动化

--json 让每条匹配变成一行 JSON,非常适合在脚本里二次处理:

# 找出所有 TODO 并输出文件路径 + 行号
rg --json "TODO" --no-heading | jq -r 'select(.type=="match") | .data.path.text + ":" + (.data.lines.text|tostring)'

六、进阶:正则、上下文与性能

# 固定字符串搜索(把模式当字面量,避免正则元字符报错)
rg -F "a.b.c"        # 只匹配字面的 a.b.c

# 前后各 3 行上下文
rg -C 3 "panic"

# 整词匹配(边界)
rg -w "log"

# 看看这次搜索花在哪:文件数、匹配数、耗时
rg --stats "serialize"

ripgrep 的正则基于有限自动机,遇到 (a+)+ 这类“灾难性回溯”模式也不会卡死——这是它相对很多 PCRE 引擎的安全优势。需要更激进的 PCRE2 语法时,可加 -P(编译时启用了 PCRE2 的版本才支持)。

七、在 CI 与编辑器里用起来

ripgrep 不只是本地利器,它早已是很多工具的“心脏”。例如 VS Code 的全局搜索 底层就是 ripgrep——你在编辑器里按 Ctrl/Cmd+Shift+F 搜的每一行,背后都是 rg 在跑。理解了这一点,你在终端和编辑器里的搜索行为就能完全对齐。

GitHub Actions 流水线 里,ripgrep 也能做轻量级“规范门禁”:

# 在 CI 中禁止遗留的调试代码合入
- name: Lint guard
  run: |
    if rg -l "console.log|debugger|TODO:REMOVE" src/; then
      echo "发现疑似调试代码,请清理后再提交"
      exit 1
    fi

八、ripgrep 与 git:天生一对

ripgrep 默认就“懂” git:它会读取 .gitignore、.git/info/exclude 以及全局 gitignore,自动把第三方依赖和构建产物排除在搜索范围之外。这意味着你在项目根目录直接敲 rg "xxx",结果天然干净,不会像 grep 那样把 node_modules 里的几万个文件也翻一遍。如果你就是想搜被忽略的文件(比如确认某个密钥有没有意外提交),加 -u 即可逐层放开;反之想完全无视所有忽略规则、连隐藏文件一起扫,用 -uuu

# 默认忽略 .gitignore 中的文件(不会进 node_modules)
rg "api_key"
# 额外搜索隐藏文件
rg -u "api_key"
# 搜索一切文件(含被 ignore 的、含二进制)
rg -uuu "api_key"
# 在 git 仓库里只搜已跟踪文件
git ls-files | rg "test"

九、固化配置:~/.ripgreprc

每次都敲一长串参数很烦。ripgrep 支持读取 RIPGREP_CONFIG_PATH 指向的配置文件,把常用开关固化下来,团队新人拉一份配置就能获得一致的搜索体验:

# ~/.ripgreprc
--smart-case          # 含大写才区分大小写,全小写则忽略大小写
--line-number
--color=auto
# 自定义类型:把 vue 也算作前端文件
--type-add
web:*.{html,js,ts,vue,css}

设置环境变量 export RIPGREP_CONFIG_PATH="$HOME/.ripgreprc" 后,rg 会自动套用这些选项,日常调用只需写最精简的模式即可。

十、一次真实重构的搜索流

假设你要给一个老服务做“日志组件统一”重构,流程大概是:先 rg -l "console.log" 圈出所有散落的打印点,再用 rg -t ts -C 2 "console.log" 逐个看上下文,确认后批量改成统一 logger;最后用 rg -l "console.log" 反过来验证“零残留”。这一套“圈范围 → 看上下文 → 验证清零”的闭环,正是 ripgrep 最舒服的用法。

# 1) 圈出范围
rg -l "console.log" src/
# 2) 看上下文(前后 2 行)
rg -t ts -C 2 "console.log" src/
# 3) 替换并验证清零
rg -l "console.log" src/ | xargs sed -i '' 's/console\.log/logger.info/g'
rg -l "console.log" src/   # 期望无输出

十一、常见坑与排雷 checklist

  • 搜不到?先确认是不是被 .gitignore 或隐藏规则过滤了,加 -u(甚至 -uuu)重试。
  • 想搜中文/Unicode?ripgrep 默认 UTF-8,直接搜即可;文件编码非 UTF-8 时用 --encoding
  • 误以为 rg 会改文件?rg 只读不写,-r 只是“输出替换结果”,真正改写请用 sed/IDE。
  • 正则不生效?注意 shell 引号;含 $* 等建议用单引号包裹模式。
  • 性能异常慢?多半是 -u 穿透了 node_modules 之类巨型目录,用 -g 收缩范围。

结语

ripgrep 是典型的“装上就回不去”的工具:零配置、速度快、默认行为符合直觉。把它和 后端命令行效率工具 里的其他利器组合,你的终端检索效率会直接上一个台阶。今天就可以在常用项目里跑一次 rg "TODO",看看那些被遗忘的待办。

上一篇 磁盘 inode 耗尽排查实战:服务诡异故障的根因定位
下一篇 Feature Flag 实战:用特性开关安全发布新功能