grep 可能是 Linux 下使用频率最高的命令之一。它的全名是 Global Regular Expression Print,本质就是”在文本里找匹配的行”。但真正用熟之后,它能解决的问题远比”搜关键字”多。
这篇文章按使用场景组织,从最常用的写法讲到几个真正能省时间的高级技巧。
一、最常用的几个参数(90% 的场景靠它们)
记住下面这五个参数,日常排查基本够用:
| 参数 | 作用 | 例子 |
|---|---|---|
-r | 递归搜索子目录 | grep -r "TODO" ./src |
-n | 显示行号 | grep -n "error" app.log |
-i | 忽略大小写 | grep -i "warning" app.log |
-v | 反向匹配(不包含) | grep -v "DEBUG" app.log |
-E | 启用扩展正则 | grep -E "error|fail" app.log |
这五个可以组合用,比如最常见的:
grep -rn "deny" /etc/nginx/含义是:在 /etc/nginx/ 下递归搜索所有包含 deny 的行,并显示文件名 + 行号。这就是上一段对话里用到的命令——排查 nginx 403 时,能瞬间定位到是哪个配置文件的哪一行加了拦截规则。
二、运维 / SRE 排查场景
1. 从日志里捞错误
# 找出所有 error 行grep -i "error" /var/log/nginx/error.log
# 排除某些已知的噪声(比如健康检查)grep "error" app.log | grep -v "healthcheck"
# 找 error,但同时显示前后 3 行的上下文grep -C 3 "error" app.log
# 只看前面 2 行(Before)或后面 2 行(After)grep -B 2 "error" app.loggrep -A 2 "error" app.log-A / -B / -C 这组上下文参数极其有用。报错往往不在那一行本身,而在它的上下文里——比如一个 stack trace,或者前一条请求的参数。
2. 在配置目录里找问题
# 找出谁在哪里设了某个变量grep -rn "max_connections" /etc/
# 同时搜多个关键词(用 -E + 管道符)grep -rnE "deny|allow" /etc/nginx/
# 搜配置时跳过注释行grep -v "^\s*#" nginx.conf | grep -v "^\s*$"最后这个组合非常实用:先去掉注释行,再去掉空行,剩下的就是配置文件里真正生效的内容。看一份几百行的 nginx.conf 时能省下大量精力。
3. 统计与计数
# 统计某个关键词出现了多少次(实际是有多少行)grep -c "404" access.log
# 统计某个 IP 访问了多少次grep -c "192.168.1.100" access.log
# 找出文件里出现过哪些 IP(去重 + 排序 + 计数)grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" access.log | sort | uniq -c | sort -rn | head-o 参数表示”只输出匹配到的部分,而不是整行”。这在做日志分析时几乎是必备技巧。
三、后端开发查代码场景
1. 在项目里找一个函数 / 变量被谁用了
# 在当前目录递归搜grep -rn "getUserById" .
# 只看 .go 文件(用 --include)grep -rn --include="*.go" "getUserById" .
# 排除 node_modules、vendor、.gitgrep -rn --exclude-dir={node_modules,vendor,.git} "getUserById" .
# 只显示文件名,不显示具体行(用 -l,方便后续批量处理)grep -rln "getUserById" ./src--exclude-dir 强烈推荐设成习惯。在前端项目里不加这个,光 node_modules 就能让 grep 跑半分钟。
2. 找 TODO / FIXME / HACK
grep -rnE "(TODO|FIXME|HACK|XXX)" --exclude-dir={node_modules,.git} ./src很多团队会把这条命令做成 git pre-commit hook 或 CI 检查。
3. 找出某个 API 调用在哪里发起
# 找所有调用了 /api/v1/users 的地方grep -rn "/api/v1/users" ./src
# 区分 GET 和 POSTgrep -rnB 2 "/api/v1/users" ./src | grep -E "(GET|POST|fetch|axios)"四、几个真正进阶的用法
1. -P 启用 Perl 正则(支持零宽断言等高级语法)
# 找出所有以数字开头但不以数字结尾的行grep -P "^\d.*[^\d]$" data.txt
# 提取 JSON 里某个字段的值(简单场景)grep -oP '"user_id":\s*"\K[^"]+' data.json\K 的含义是”匹配但不输出前面的部分”。配合 -o,可以在没有 jq 的时候临时提取字段值。
⚠️ macOS 自带的 BSD grep 不支持
-P,需要brew install grep装 GNU 版本,再用ggrep。
2. -f 从文件里读取要搜的关键词列表
# patterns.txt 每行一个关键词grep -f patterns.txt access.log适合做黑名单 / 白名单批量过滤。比如有一份”恶意 IP 列表”,想从访问日志里找出所有命中的请求,一行命令就够了。
3. -w 全词匹配,避免误伤
# 不加 -w:会匹配到 user、users、usernamegrep "user" code.py
# 加 -w:只匹配独立的 user 这个词grep -w "user" code.py查变量名、函数名时几乎必加,否则结果里全是噪音。
4. 结合 xargs 做批量操作
# 找出所有包含 "deprecated_func" 的文件,并用 vim 打开grep -rln "deprecated_func" ./src | xargs vim
# 找出所有 .log 文件里的 error,超过 10 个的归档grep -lc "error" *.log | awk -F: '$2 > 10 {print $1}' | xargs -I{} mv {} archive/五、性能小贴士
文件特别大(几个 GB 的日志)时,几个能加速的小技巧:
- 用
LC_ALL=C:把 locale 切成 C,跳过 UTF-8 解码,速度能快好几倍Terminal window LC_ALL=C grep "error" huge.log - 用
-F做纯字符串匹配:不需要正则时,-F(fixed string)比默认快Terminal window grep -F "192.168.1.100" access.log - 用
ripgrep(rg) 替代:基于 Rust 写的现代版 grep,默认就跳过.gitignore里的文件,速度也快得多。日常代码搜索强烈推荐Terminal window rg "getUserById" --type go
六、一张速查表
把下面这张表存到笔记里,需要时复制粘贴就行:
# 基础grep "pattern" file # 单文件搜索grep -rn "pattern" dir/ # 递归 + 行号grep -i "pattern" file # 忽略大小写grep -v "pattern" file # 反向匹配grep -w "pattern" file # 全词匹配grep -c "pattern" file # 计数grep -l "pattern" * # 只列文件名
# 上下文grep -A 3 "pattern" file # 后 3 行grep -B 3 "pattern" file # 前 3 行grep -C 3 "pattern" file # 前后各 3 行
# 正则grep -E "a|b" file # 扩展正则(or)grep -P "\d+" file # Perl 正则grep -o "pattern" file # 只输出匹配部分
# 范围控制grep -r --include="*.py" "x" . # 只搜 .pygrep -r --exclude-dir=node_modules "x" . # 排除目录
# 组合grep -rnE --exclude-dir={.git,node_modules} "TODO|FIXME" .写在最后
grep 的强大之处不在于参数多,而在于它能和管道、xargs、awk、sort、uniq 等工具自然组合,形成一套排查问题的”瑞士军刀”。
排查问题时,与其打开一个图形化日志查看器慢慢翻,不如先用一行 grep -rn 把问题缩小到具体的几行——往往几秒钟就能定位到根因。
这也是为什么老 SRE 都说:“一个会写 grep 的人,调试速度至少快 3 倍。”