Linux grep 命令:搜索文本、正则表达式与筛选输出
grep 用来从文件或命令输出中找出符合指定模式的行。最基本的用法是 grep '模式' 文件名;它还支持忽略大小写、反向选择、递归搜索、正则表达式以及显示匹配行的上下文。
模式通常用单引号括起来,避免 shell 先解释其中的星号、美元符号等字符。不过,引号只控制 shell 的解释,并不会让正则表达式自动变成普通字符串;需要按字面查找时使用 -F。
grep 是什么命令?
grep 按行读取输入,默认输出包含匹配内容的整行。名称源自 Unix 文本编辑器 ed 的 g/re/p 表达式,即在整个范围内寻找符合正则表达式的内容并打印。
不指定文件时,通常读取标准输入,因此可以通过管道接收另一个命令的输出。GNU grep 默认使用基本正则表达式(Basic Regular Expression,BRE);-E 切换为扩展正则表达式(Extended Regular Expression,ERE)。本文介绍 GNU grep;其他实现对扩展选项的支持可能不同。
基本语法与第一次搜索
下面是语法示意,方括号表示可选项,不要原样输入:
grep [option] 'pattern' [file...]
搜索前先准备可读取的文本文件。以下例子假定当前目录已有相应日志或配置文件;请替换为实际路径。找出 app.log 中含有大写 ERROR 的行:
grep 'ERROR' app.log
指定多个文件时,结果通常带有文件名前缀,便于定位来源:
grep 'ERROR' app.log worker.log
标准输入也可以搜索。例如,下面筛选内核消息中含有 usb 的行,并忽略大小写:
dmesg | grep -i 'usb'
部分 Linux 系统限制普通用户读取内核日志。若 dmesg 报权限错误,grep 无法解决上游读取问题,应按系统权限政策处理。管道也不会自动把上游的标准错误当作搜索文本。
常用 grep 选项
| 选项 | 作用 | 例子 |
|---|---|---|
-i |
忽略大小写 | grep -i 'error' app.log |
-v |
选择不匹配的行 | grep -v '^#' config |
-n |
显示行号 | grep -n 'TODO' main.c |
-w |
按单词边界匹配 | grep -w 'cat' words.txt |
-x |
要求整行匹配 | grep -x 'enabled' status.txt |
-F |
使用固定字符串 | grep -F 'a.b' data.txt |
-E |
使用扩展正则表达式 | grep -E 'error|failed' app.log |
-r |
递归搜索目录 | grep -r 'listen' /etc/nginx |
-l |
只输出包含匹配的文件名 | grep -rl 'deprecated' src |
-c |
统计每个文件的匹配行数 | grep -c 'ERROR' app.log |
-A / -B / -C |
显示后方、前方或两侧上下文 | grep -C 2 'panic' app.log |
选项可以组合,例如 grep -in 'error' app.log 同时忽略大小写并显示行号。完整的选项和适用条件可查阅GNU Grep 3.12 官方源代码包中的 doc/grep.texi 说明书。
区分固定字符串与正则表达式
用 -F 按字面搜索
点、星号、方括号在正则表达式中具有特殊含义。只想寻找文字本身时,使用 -F;下面的点表示真正的点:
grep -F '192.168.0.1' access.log
这会选择包含该字符串的行,并不自动保证完整 IP 地址相等。例如,更长的地址也可能包含它;需要精确字段判断时,还要结合日志格式限定边界。
用 -E 表达多个正则条件
-E 可以直接使用选择符 |、重复符 +、? 和括号。下面找出含有 ERROR、WARN 或 FATAL 任意一项的行:
grep -E 'ERROR|WARN|FATAL' app.log
GNU grep 的 BRE 与 ERE 主要在这些运算符的写法上不同。迁移到其他系统时,应检查实际 grep 实现,而不是假定所有扩展选项都通用。
限定行首、行尾与空行
^ 表示行首,$ 表示行尾,^$ 匹配没有字符的空行。下面三个命令分别搜索以 server 开头、以 completed 结尾的行,以及空行:
grep '^server' config.txt
grep 'completed$' job.log
grep '^$' notes.txt
配置文件中还可能有只含空白的行。下面依次排除前面可带空白的注释行,以及只含空白的行:
grep -v '^[[:space:]]*#' app.conf | grep -v '^[[:space:]]*$'
这不是通用的配置文件解析器:引号中的井号、行内注释等规则取决于文件格式,不能仅靠这两个表达式完整处理。
准确限定匹配范围
整个单词与整行匹配
-w 检查匹配内容两侧的单词边界,因此 cat 通常不会匹配 category 中的 cat。GNU grep 把字母、数字和下划线视为单词组成字符;这不是中文自然语言分词器,字符分类也受区域设置影响。
grep -w 'root' /etc/passwd
该命令搜索 /etc/passwd 中边界独立的 root,并非严格限定账户名字段。-x 则要求模式覆盖整行:
grep -x 'active' service-status.txt
文件中的行如果是 active ,尾部有空格,就不符合这里的 active。-x 仍把模式当作正则表达式;需要整行固定字符串相等时可组合 -Fx。
选择不匹配的行
-v 反转选择,下面输出不含大写 DEBUG 的行:
grep -v 'DEBUG' app.log
同时忽略大小写:
grep -iv 'debug' app.log
一次搜索多个模式
重复 -e 表示多个候选模式,任意一个匹配即可选中该行,不要求所有模式同时出现:
grep -e 'ERROR' -e 'FATAL' app.log
模式较多时,可将它们逐行写入 patterns.txt,再通过 -f 读取:
grep -f patterns.txt app.log
例如文件中分别写 ERROR 和 FATAL 两行,就相当于上面的两个 -e。注意:模式文件中的空白行代表空模式,能匹配每一行;完全空的模式文件则不匹配任何行。-Ff 可将各模式按固定字符串处理。
递归搜索整个目录
-r 与 -R 的区别
-r 递归读取指定目录下的文件。GNU grep 会跟随命令行直接指定的符号链接,但跳过递归遍历时遇到的其他符号链接:
grep -rn 'database_url' ./config
-R 则会跟随遍历过程中遇到的符号链接,可能进入原先未打算搜索的目录或遇到循环链接。因此应明确起点和访问范围,通常先使用 -r。
包含或排除特定文件
--include 与 --exclude 按文件名通配模式筛选,区别于搜索内容的正则表达式。引号防止 shell 提前展开星号。下面分别搜索 .conf 文件,以及排除压缩的 .min.js 文件:
grep -rn --include='*.conf' 'timeout' /etc
grep -rn --exclude='*.min.js' 'deprecated' ./src
第一条会遍历 /etc,可能比较慢或遇到权限错误;已知配置目录时应缩小范围。排除整个目录可用 --exclude-dir:
grep -rn --exclude-dir='.git' --exclude-dir='node_modules' 'api_key' .
这里的 api_key 是演示搜索词。实际输出可能包含密钥或其他配置值,分享结果前应检查并遮盖敏感信息。不要通过丢弃所有标准错误来假装搜索完整成功。
只列出文件名
只想知道哪些 HTML 文件包含 jb-heading,可使用 -l;发现该文件的首个匹配后就不必继续扫描其余内容:
grep -rl --include='*.html' 'jb-heading' .
大写 -L 列出没有匹配的文件:
grep -rL --include='*.html' 'jb-heading' .
搜索结果为空之前,应确认文件确实可读取、筛选条件确实包含预期文件;读取失败不等于内容中没有该模式。
让搜索结果更易读
显示行号与文件名
-n 显示从 1 开始的行号;即使只搜索一个文件,-H 也强制显示文件名。需要隐藏文件名时使用 -h:
grep -nH 'listen' nginx.conf
同时查看上下文
错误行不足以解释问题时,可以附带周围内容。下面三种命令是替代用法,可按需要选择:
grep -A 3 'ERROR' app.log
grep -B 2 'ERROR' app.log
grep -C 2 'ERROR' app.log
-A 3 附加后 3 行,-B 2 附加前 2 行,-C 2 附加前后各 2 行。上下文重叠时会合并,并非把同一行反复输出;分开的结果组默认用双连字符分隔。
只输出匹配部分
-o 只输出匹配到的非空片段,各片段分别占一行。下面从文本中提取三位数字、连字符、四位数字组成的片段:
grep -oE '[0-9]{3}-[0-9]{4}' contacts.txt
它不校验完整电话号码,也不保证片段两侧没有其他数字。终端中可使用 --color=auto 高亮;自动模式通常只在输出到终端时加颜色,重定向给文件或管道时使用 auto 或 never,避免混入颜色控制字符。
筛选日志和命令输出
从最近日志中查找错误
只检查末尾 500 行,可先用 tail 缩小范围,再忽略大小写搜索多个错误词:
tail -n 500 app.log | grep -iE 'error|failed|fatal'
持续筛选日志追加内容时可以使用:
tail -F app.log | grep --line-buffered 'ERROR'
tail -F 会按文件名跟踪并重试打开文件,默认先输出末尾 10 行,而非只显示启动之后的新内容。GNU grep 的 --line-buffered 在每个输出行后刷新缓冲,但无法消除上游缓冲、文件轮转检测或尚未换行造成的延迟。按 Ctrl+C 结束前台跟踪。轮转及跟踪条件见中文 tail 命令说明。
筛选服务与进程结果
下面从 systemd 的单元文件列表中筛选含有 nginx 的文本行:
systemctl list-unit-files | grep 'nginx'
systemctl 官方手册源文件区分单元文件列表与运行状态:该列表用于查看安装及启用状态,不能仅凭匹配结果断言服务正在运行。若只想找进程,支持 procps pgrep 的系统可以使用:
pgrep -af 'nginx'
-f 搜索完整命令行,-a 同时显示 PID 和命令行;模式通常是扩展正则表达式,可能匹配参数中含 nginx 的其他进程。这仍不是服务健康检查,也不代表端口可用。具体选项见 procps-ng 的 pgrep 官方手册源文件。
区分匹配行数与匹配次数
-c 按文件统计选中的行。同一行出现两次 ERROR,仍只计为一行。例如日志只有 ERROR ERROR 和 INFO 两行时,下面命令输出 1;后面的 -o 与 wc 组合则输出 2:
grep -c 'ERROR' app.log
对于这里的非空固定词 ERROR,可用 -o 将每个匹配片段分开,再统计输出行数:
grep -o 'ERROR' app.log | wc -l
一般正则表达式的匹配可能重叠或为空;grep -o 不会枚举所有重叠匹配,也不输出空匹配。管道默认的退出状态还取决于最后一个命令,wc 输出 0 本身不能证明 grep 没有读取错误。
只检查是否存在匹配
-q 不输出匹配内容,适合条件判断。下面是 shell 条件语句,匹配 ready 后打印示例消息:
if grep -q 'ready' status.log; then
echo 'Service ready'
fi
Service ready 的意思是“服务已就绪”,但该示例实际只检查日志文字;历史记录中出现 ready 不足以证明当前服务状态。此简写也把“无匹配”和“读取错误”都走到不执行 then 的分支。需要自动化区分时,应立即保存 grep 的退出状态并分别处理。
理解 grep 的退出状态
GNU grep 通常用下面的退出状态报告结果:
| 状态 | 含义 | 处理方式 |
|---|---|---|
0 |
至少选中一行 | 按找到结果处理 |
1 |
没有选中行 | 可能是正常的无匹配结果 |
2 |
发生错误 | 检查路径、权限、选项或模式 |
因此“没有结果”与“程序出错”要分开判断。GNU grep -q 一旦找到匹配即可返回 0,即使此前检测到其他错误;若必须确认所有输入都读取成功,不应依赖 -q。其他 grep 实现也可能用大于 2 的状态表示错误。使用 -v 时,“选中”指的是不匹配原模式的行。
常见问题与处理方法
模式以连字符开头
像 -ERROR 这样的模式可能被误认作选项。可以先用 -- 结束选项列表:
grep -- '-ERROR' app.log
也可以用 -e '-ERROR' 明确指定模式。以连字符开头的文件名同样需要考虑选项解析。
出现 Binary file matches 提示
GNU grep 根据空字节、当前字符编码等信息判断二进制输入,有时只提示匹配而不直接打印内容。确认数据适合按文本处理时,可用 -a:
grep -a 'ERROR' mixed-data.log
这可能输出空字节或终端控制字符,不要对未知二进制数据盲目使用。-I 可把检测出的二进制输入按无匹配处理,常用于递归搜索;它是 grep 的处理策略,不是通用文件类型鉴定器。
正则特殊字符导致结果不同
普通的点匹配任意单个字符,星号表示前一项重复零次或多次,不是文件名通配符的用法。只想搜索文字本身就用 -F;需要正则表达式则保留正确语法并用单引号隔离 shell 展开。单双引号和正则语法属于不同层次。
递归搜索太慢或结果过多
先缩小起始目录,再使用 include、exclude、exclude-dir 排除无关文件。检查输出中的权限和读取错误;隐藏这些消息会让搜索看似干净,却可能漏掉本来需要检查的数据。必要时先在一个已知文件上确认大小写、编码和表达式,再扩大范围。
常见问答
grep 默认区分大小写吗?
区分。使用 -i 忽略大小写,但字符大小写转换会受区域设置和字符集影响,并非所有语言都等同于简单的英文字母转换。
grep -r 和 grep -R 有什么区别?
GNU grep 中两者都递归搜索。-r 跟随命令行明确指定的符号链接,跳过遍历中遇到的其他链接;-R 跟随所有符号链接,搜索范围可能更广。
没有结果是否表示错误?
不一定。通常状态 1 表示无匹配,状态 2 表示错误;还要留意 -q 找到匹配后返回 0 的例外,以及管道上游的失败。
如何找出包含多个词中任意一个的行?
用 grep -E 'error|warning' 文件名,或重复 -e,例如 grep -e 'error' -e 'warning' 文件名。多个普通字符串可组合 -F 与多个 -e。
使用要点
字面搜索优先考虑 -F,多条件正则搜索使用 -E,目录搜索从明确范围的 -r 开始。用 -n 与 -C 便于定位,用 -l 获取文件列表,用 -q 判断是否存在匹配。把模式的 shell 引号、grep 的正则语法和退出状态分别考虑,才能正确解释搜索结果。




