grep 命令详解
更新时间:2026-08-28。本文是「一个命令一个文档」系列的第一篇,聚焦
grep——它在 Linux 命令速查里属于文本处理与搜索族,但用法之多、场景之广,值得单独成篇。
为什么先写 grep?因为排查问题的肌肉记忆往往是这样的:服务报错了,第一反应不是打开编辑器,而是 grep error /var/log/xxx.log。它几乎是每个 Linux 用户用得最勤的命令,没有之一。
grep(Global Regular Expression Print,全局正则表达式打印)按正则表达式(regex,用特殊字符描述字符串匹配模式的语法)搜索文本,把匹配的行打印出来。它不修改文件,只做"找",是 Unix 管道里最典型的过滤器。
一、最基本的用法
最简形态就是「grep 模式 文件」:
grep "error" app.log # 在 app.log 里找含 error 的行
grep "404" access.log # 找所有 404 请求
grep root /etc/passwd # 不用引号也行,模式里没空格时常见如果模式或文件里有空格、特殊字符,记得加引号——这是新手最容易踩的坑之一:
grep 'timeout: 30' config.yaml # 模式含空格,必须引号一个常被忽略的点:
grep的退出码(exit code)可以直接用于判断。找到匹配返回 0,没找到返回 1,出错返回 2。所以脚本里写if grep -q "OK" status.txt; then ...是标准写法,-q表示静默、只关心成不成功不打印结果。
二、高频选项一张表
下面这些选项,构成了 grep 日常用法的 90%:
| 选项 | 作用 | 示例 |
|---|---|---|
-i | 忽略大小写 | grep -i error log(匹配 Error/ERROR/error) |
-r / -R | 递归搜索目录 | grep -r "TODO" src/ |
-n | 显示行号 | grep -n "main" app.py(定位到第几行) |
-v | 反选,排除匹配行 | grep -v "^#" config(去掉注释行) |
-c | 只计数匹配行数 | grep -c "error" log(共多少行出错) |
-l | 只列文件名 | grep -l "bug" *.py(哪些文件含 bug) |
-E | 扩展正则(等同 egrep) | grep -E "err|warn" log |
-F | 固定字符串,不做正则 | grep -F "a.b" file(点号就是点号) |
-A n / -B n / -C n | 显示匹配行后/前/前后 n 行上下文 | grep -A3 -B3 "panic" log |
-w | 全字匹配 | grep -w "cat" file(不匹配 category) |
-o | 只输出匹配的部分 | grep -oE "[0-9]{1,3}\.[0-9]{1,3}"(提取 IP) |
-q | 静默,用于脚本判断 | if grep -q foo file; then |
-m n | 最多匹配 n 次就停 | grep -m 1 "seed" log(只取首次) |
-v是过滤噪音的利器。ps aux | grep nginx | grep -v grep这条组合能排掉 grep 进程自己;grep -v "^#" | grep -v "^$"则是"去注释去空行"的标配。记住-v的反向思维,比记住十个-i都实用。
三、正则表达式:grep 的灵魂
grep 默认用基础正则(BRE),很多元字符需要转义;加 -E 切到扩展正则(ERE),写起来舒服很多。对比一下:
# 基础正则:| 和 + 要转义
grep "err\|warn" log
grep "colou*r" file
# 扩展正则:直接写
grep -E "err|warn" log
grep -E "colou*r" file常用的正则元字符:
| 元字符 | 含义 | 示例 |
|---|---|---|
. | 任意单个字符 | grep "a.c" 匹配 abc、a c |
* | 前一个字符重复 0 次或多次 | grep "ab*c" 匹配 ac、abc、abbc |
^ / $ | 行首 / 行尾 | grep "^#" 注释行;grep "done$" 以 done 结尾 |
[ ] | 字符集合 | grep "[0-9]" 含数字的行 |
| | 或(需 -E 或转义) | grep -E "get|post" |
+ | 重复 1 次以上(需 -E) | grep -E "go+gle" |
{n,m} | 重复 n 到 m 次(需 -E) | grep -E "[0-9]{1,3}" |
新手常犯的错:想匹配字面量的
.却写成grep "a.c"结果把axc也匹配了。要匹配真正的点号,用grep -F固定字符串,或者转义grep "a\.c"。正则很强大,但也最容易因为"少写一个反斜杠"而匹配到意料之外的结果——先用-n看行号确认一下再放心。
四、和 find / xargs 的组合
grep 单打独斗够用,但配合 find 才能精准定位"某个类型的文件里"的内容:
# 在最近 7 天改过的 .py 文件里找 print
find . -name "*.py" -mtime -7 | xargs grep "print"
# 等价写法,处理文件名含空格更安全
find . -name "*.py" -print0 | xargs -0 grep "print"
# 递归搜但跳过 .git 和 node_modules
grep -r --exclude-dir=.git --exclude-dir=node_modules "TODO" .--include / --exclude / --exclude-dir 这套排除参数,在工程目录里搜代码时几乎必用——否则 .git 里的历史版本、依赖目录的几千个文件都会被拉进来,慢且吵。
五、性能:grep vs ripgrep
当目录很大时,传统 grep -r 会明显变慢。这正是 现代命令行工具 里 rg(ripgrep)出现的原因——它默认递归、自动跳过 .gitignore 里的文件、用 Rust 写的多线程匹配,速度常快一个数量级。
rg "error" . # 比 grep -r 快很多,且默认忽略 .gitignore
rg -i "timeout" src # -i 同样忽略大小写不是说
grep要被淘汰。在容器、最小系统、CI 环境里,grep几乎一定在,而rg未必装了。我的习惯是:本地开发用rg提速,写进脚本或文档给别人复现时用grep保兼容。两者语法高度重合,会一个就会另一个。
六、实战:一次日志排查
假设线上 API 偶发 5xx,要从几十个滚动日志里揪出异常。典型流程:
# 1. 先数一下 5xx 有多少
grep -c " 5[0-9][0-9] " access.log*
# 2. 看这些错误前后的上下文
grep -C5 " 500 " access.log.2026-08-28 | head -50
# 3. 按小时聚合错误数,定位爆发时段
grep " 500 " access.log* | awk '{print $4}' | cut -d: -f2 | sort | uniq -c
# 4. 只保留出错请求的 URL,去重统计
grep " 500 " access.log* | grep -oE '"/[^"]*"' | sort | uniq -c | sort -rn | head这条链路把 grep(找)→ awk/cut(取字段)→ sort/uniq(聚合)串起来,正是 Unix 管道哲学的标准打法。想深入文本加工,见 Shell 文本处理。
七、进阶:把一次日志排障拆成管道
grep 很少孤立使用。下面这条链路是排障时最典型的"过滤 → 抽取 → 聚合"三段式,每一段都能单独替换成别的工具:
# 找 5xx 错误 → 抽 IP → 按 IP 计数排序 → 看 Top 来源
grep " 5[0-9][0-9] " access.log \
| grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' \
| sort | uniq -c | sort -rn | head为什么不直接用一条 awk 搞定?因为分段调试更稳——任一段结果不对,单独跑前面那段看输出。等确信对了,再合并成一行。这条思路比"一句神级正则"更抗改需求。
上图就是一次排障的数据流向:先收窄(过滤),再抽取(正则),最后聚合(排序计数)。把"找问题"变成可重复的流水线,下次换关键词就能复用。
八、容易忽略的细节
--include/--exclude的匹配对象是文件名而非路径:想排除test/目录要用--exclude-dir=test,写--exclude=test/*往往不生效。- 二进制文件会输出乱码匹配:加
-I(大写 i)跳过二进制,或--binary-files=without-match。 - 中文/多字节匹配:默认按字节匹配,遇到 UTF-8 多字节字符的正则要用
grep -P(PCRE)才稳妥,但-P在个别发行版是"实验性";更通用做法是交给 awk 或 ripgrep。 grep -r默认跟随符号链接会死循环:大目录加--no-dereference或用rg(ripgrep 默认不跟 symlink)。
相关命令
一句话总结
grep = 用正则表达式在文本里"找行"的过滤器,靠 -i/-r/-v/-n/-E/-A/-B 覆盖九成场景,退出码让它能进脚本判断,配合 find/xargs 可精准搜工程目录;大目录换 rg 提速,但 grep 因兼容性仍是脚本首选。把 grep 练熟,Linux 排障就跨过了第一道门槛。