Skip to main content

Linux grep / rg / glob 命令是做什么用的?

· 6 min read

grep 搜文件内容、rg 是 grep 的现代升级、glob 匹配文件名——三者名字长得像,但 各管一摊

  1. grep:POSIX 自带文本搜索,-r/-n/-i/-E 是高频选项
  2. rg (ripgrep):Rust 写的现代版,默认跳过 .gitignore 和二进制
  3. glob:shell 通配符(*/?/[...]),没加引号会被 shell 展开
  4. globstarshopt -s globstar 后,** 才递归匹配任意层级
  5. find -name:find 自己的类 glob 语法,不走 shell 展开
  6. 别乱混grep *.js 被 shell 拆成多文件,只搜当前目录

三个工具各管什么

工具搜的对象是不是命令默认行为
grep文件内容是(POSIX 自带)只搜当前文件,要 -r 才递归
rg文件内容是(要装)自动递归,跳过 .gitignore 和二进制
glob文件名不是,是 shell 功能命令运行前由 shell 自动展开

glob 不是命令,是 bash/zsh 自带的 通配符匹配规则——你按下回车之前,shell 就把 * ? 这些先展开成实际的文件名,再喂给命令。所以 grep "TODO" *.js 里的 *.js 不是 grep 处理的,是 shell 处理的。

grep:经典文本搜索

POSIX 标准工具,所有 Linux/macOS/Unix 发行版都有,不用装。基础用法一行:

grep "pattern" file.txt

日常 90% 的场景用这几个选项就够:

选项作用
-r递归子目录
-n显示行号
-i忽略大小写
-v反向(输出不匹配的行)
-c统计匹配行数
-l只列出文件名
-E启用扩展正则(等同于 egrep)
-w全词匹配
-A/-B/-C N匹配行后/前/上下 N 行
--include="*.js"只搜特定后缀
--exclude-dir=node_modules排除目录

实战:搜项目里所有 TODO:

grep -rn --include="*.js" --exclude-dir=node_modules "TODO" src/

Git 项目用 git grep:只搜 git 跟踪的文件,比系统 grep 快,但功能跟 grep 一致。

最容易踩的坑:shell 会提前展开你的通配符

grep "TODO" *.js

按下回车的瞬间,shell 会先把 *.js 展开成 a.js b.js c.js(假设当前目录有这三个文件),然后命令变成:

grep "TODO" a.js b.js c.js

只在当前目录生效,不会递归到子目录——很多人以为是 grep 在搜所有 JS 文件,其实是 shell 在帮你"挑文件",grep 根本不知道有 *.js 这种语法。

想递归搜子目录别用 glob,让 grep 自己来:

# 错的:shell 展开后还是只在当前目录
grep -r "TODO" *.js

# 对的:让 grep 自己递归
grep -rn "TODO" --include="*.js" .

下节介绍的 rg 用 -g "*.js" 限制文件类型,能一行搞定同样的事。

rg (ripgrep):现代首选

ripgrep 是用 Rust 写的 grep 替代品,作者 BurntSushi。装一下:

brew install ripgrep # macOS
apt install ripgrep # Ubuntu 20.04+

相对 grep 的核心优势:

  1. 跳过 .gitignore:默认不搜 node_modulesdisttarget,不会卡死
  2. 跳过二进制:不会把 PNG/PDF 当文本乱匹配
  3. 自动递归:不需要 -r,默认就递归
  4. 并行搜索:Rust + 多线程,比 grep 快几倍
  5. 智能大小写:搜索词全小写忽略大小写,含大写严格匹配
  6. 文件类型过滤-t js -t rust -t py 不用写 glob
  7. Unicode 默认正确:中文匹配不会乱

实战对比:

# grep:手动排除 node_modules + -r
grep -rn --include="*.js" --exclude-dir=node_modules "TODO" .

# rg:默认就跳过 node_modules
rg "TODO" -t js

我之前用 grep 搜一个 monorepo,node_modules 卡了 30 秒;换成 rg 一秒出结果。这是装 ripgrep 最直接的理由。

偶尔需要"无视 .gitignore 全搜一遍",比如查 secret 是否泄漏:

rg -uu "secret"

-uu 是 "no ignore",禁用所有 ignore 规则。

VSCode 的全局搜索(Cmd+Shift+F)底层用的就是 ripgrep,所以它搜得快。

glob:shell 通配符

glob 是 shell 在命令执行前做的 文件名展开,跟 grep / rg 没关系。三种核心通配符:

通配符含义
*任意字符(不含 /
?单个字符
[abc]字符类,匹配 a/b/c 任一
[a-z]字符范围

日常例子:

ls *.js # 当前目录所有 .js
ls ?.txt # 当前目录所有 1 个字符的 .txt
ls [A-Z]*.md # 当前目录首字母大写的 .md
ls src/*.js # src/ 下(不含子目录)的 .js

globstar:让 ** 真正递归

bash 默认 *** 一样不跨目录。要让它递归匹配任意层级:

shopt -s globstar
ls src/**/*.js # src 下所有子目录的 .js

zsh 默认就支持 **,不用额外开。

find -name 不是 glob

最后澄清一个常被搞混的:find -name-name 后面的模式长得像 glob,但 是 find 自己在内部匹配,不走 shell 展开:

find . -name "*.js" # shell 看到引号不展开,find 自己匹配
find . -name '*.js' # 同上
find . -name *.js # 无引号,shell 会展开,但结果一样(如果当前目录没 .js)

所以 find -name 的引号是 保险 不是必须——加引号只是告诉 shell "别动这个模式"。

实战组合:

# find 找文件 + grep 搜内容(传统写法)
find . -name "*.js" -not -path "*/node_modules/*" -exec grep -n "TODO" {} +

# rg 一行搞定(现代写法)
rg "TODO" -t js

小结

  • grep:POSIX 自带,能用但得手动 --exclude-dir 排 node_modules
  • rg:现代首选,装上 ripgrep 后基本可以扔掉 grep
  • glob:shell 通配符,命令运行前被 shell 自动展开,记得加引号

日常排查代码,装上 ripgrep 就完事了,grep 只在远程服务器没法装东西时用一下。