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 的正则语法和退出状态分别考虑,才能正确解释搜索结果。