掌握 grep 与正则表达式:高效文本搜索实战

系统梳理 grep 的基础用法、BRE/ERE/PCRE 正则能力、性能与兼容性差异,并给出日志分析、代码搜索与数据清洗中的可直接复用命令。

阅读时长: 10 分钟
共 4562字
作者: eimoon.com

掌握 grep 与正则表达式:高效文本搜索实战

grep 是 Unix/Linux 环境中最常用的文本过滤工具之一。它的名称来自 “global regular expression print”,本质上是在输入文本中查找符合模式的内容,并输出匹配结果。这个能力看似简单,但一旦结合正则表达式、管道和脚本,就可以覆盖日志分析、代码搜索、配置审计、数据清洗等大量日常任务。

grep 的关键价值不在“查一个词”,而在于:

  • 用极低的成本构造可复用的文本过滤规则
  • 在命令行管道中充当高性能筛选器
  • 在大规模数据预处理时承担首层过滤工作
  • 在脚本中作为稳定、直接、几乎无依赖的基础组件

下文按基础匹配、正则语法、扩展能力、性能与兼容性、常见场景和排错方法展开。

准备测试数据

示例使用两个常见许可证文本文件:GPL-3BSD

在 Ubuntu 系统中,可直接复制本地已有文件:

cp /usr/share/common-licenses/GPL-3 .
cp /usr/share/common-licenses/BSD .

如果系统中没有这些文件,可下载 GPL-3

curl -o GPL-3 https://www.gnu.org/licenses/gpl-3.0.txt

也可以手动创建一个 BSD 文件:

cat << 'EOF' > BSD
Copyright (c) The Regents of the University of California.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
3. Neither the name of the University nor the names of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
EOF

grep 基础用法

字面量匹配

最基础的形式,是在文件中查找包含某个字符串的所有行:

grep "GNU" GPL-3

这里:

  • "GNU" 是匹配模式
  • GPL-3 是输入文件

输出为所有包含 GNU 的行。某些系统会自动高亮匹配片段。

常用选项

忽略大小写

grep -i "license" GPL-3

-i--ignore-case 会同时匹配:

  • LICENSE
  • license
  • License

反向匹配

grep -v "the" BSD

-v--invert-match 会输出不包含模式的行。

这里需要注意大小写。由于没有使用 -i,所以包含大写 THE 的行仍会被输出。

显示行号

grep -vn "the" BSD

-n--line-number 会在结果前输出行号。处理代码、配置文件、日志时非常实用。

正则表达式基础

grep 的强项不只是查字面文本,而是用正则表达式描述匹配规则。默认 grep 使用的是基础正则表达式,通常称为 BRE。

字面量仍然是正则

grep "GNU" GPL-3
grep "the" BSD

这类模式本身就是正则表达式,只不过它们是最简单的“字面量”。

锚点

锚点用于限定匹配位置。

行首 ^

查找以 GNU 开头的行:

grep "^GNU" GPL-3

行尾 $

查找以 and 结尾的行:

grep "and$" GPL-3

锚点在配置校验、日志格式匹配、整行过滤里非常重要。

任意单字符 .

点号 . 表示任意单个字符。

查找前面有两个任意字符,再接 cept 的内容:

grep "..cept" GPL-3

这会匹配诸如:

  • accept
  • except

字符组 []

字符组表示当前位置可以是给定集合中的任意一个字符。

查找 tootwo

grep "t[wo]o" GPL-3

取反字符组

[] 内部以 ^ 开头,表示排除这些字符:

grep "[^c]ode" GPL-3

该模式会匹配:

  • mode
  • Code 中的 ode 前字符不为小写 c 的情况

结果中如果某一整行被输出,不代表该行内所有类似字符串都匹配;只要这一行里存在任意一个命中的位置,整行就会被打印。

字符范围

查找以大写字母开头的行:

grep "^[A-Z]" GPL-3

POSIX 字符类

字符范围在某些历史排序规则或区域设置中可能存在偏差。更稳妥的写法是使用 POSIX 字符类:

grep "^[[:upper:]]" GPL-3

常见字符类包括:

  • [:upper:] 大写字母
  • [:lower:] 小写字母
  • [:alpha:] 字母
  • [:digit:] 数字
  • [:space:] 空白字符

重复零次或多次 *

* 表示前一个字符或表达式重复零次或多次。

查找带括号,且括号内部只包含字母和空格的片段:

grep "([A-Za-z ]*)" GPL-3

转义元字符

如果要匹配正则中的特殊字符本身,需要转义。常见需要转义的包括:

  • .
  • *
  • (
  • )
  • [
  • ]
  • \

查找以大写字母开头并以句点结尾的行:

grep "^[A-Z].*\.$" GPL-3

这里结尾的 \. 表示字面量句点,而不是“任意单字符”。

扩展正则表达式:-E

默认 grep 使用基础正则。更复杂的场景通常需要扩展正则表达式 ERE,可通过 -E 开启,或者使用 egrep

分组

基础正则里,分组括号通常需要转义:

grep "\(grouping\)" file.txt

使用扩展正则后可以直接写:

grep -E "(grouping)" file.txt
egrep "(grouping)" file.txt

这三种方式在这里是等价的。

选择 |

查找 GPLGeneral Public License

grep -E "(GPL|General Public License)" GPL-3

| 适合表达多个备选字符串,通常配合分组一起使用。

可选匹配 ?

? 表示前一个表达式出现零次或一次。

同时匹配 rightcopyright

grep -E "(copy)?right" GPL-3

一次或多次 +

+ 表示前一个表达式至少出现一次。

匹配 free 后面紧跟一个或多个非空白字符:

grep -E "free[^[:space:]]+" GPL-3

这会匹配:

  • free,
  • freedom
  • freedoms
  • free.

指定重复次数 {}

恰好出现指定次数

查找包含三个连续元音字母的行:

grep -E "[AEIOUaeiou]{3}" GPL-3

指定范围

查找包含 16 到 20 个字母长度单词的行:

grep -E "[[:alpha:]]{16,20}" GPL-3

进阶模式:PCRE 与 grep -P

GNU grep 在部分系统上支持 PCRE,即 Perl Compatible Regular Expressions,可用 -P 启用。它提供比 ERE 更丰富的能力,例如惰性匹配、前后查找等。

需要注意:

  • -P 是 GNU 扩展
  • 在 macOS 默认 BSD grep 中通常不可用
  • 脚本如果追求可移植性,不能默认依赖 -P

-o 只输出匹配部分

先准备一个例子文件:

echo '<a>test1</a> <a>test2</a>' > tags.html

如果使用贪婪匹配:

grep -P -o "<.*>" tags.html

输出会是整段:

<a>test1</a> <a>test2</a>

因为 .* 会尽可能长地匹配。

惰性匹配

使用 .*? 可以切换为最短匹配:

grep -P -o "<.*?>" tags.html

输出为:

<a>
</a>
<a>
</a>

对于带明显起止边界的文本片段提取,惰性匹配很实用。

前瞻与后顾

前瞻和后顾属于零宽断言,只检查上下文,不把上下文包含进最终匹配结果。

正向前瞻

只匹配后面紧跟 documentlicense

grep -P -o "license(?= document)" GPL-3

输出:

license

正向后顾

只提取 version 后面的数字:

grep -P -o "(?<=version )[0-9]" GPL-3

输出中只会包含数字本身:

3
3
3

PCRE 很强,但也带来两个代价:

  1. 可移植性下降
  2. 某些复杂模式更容易写出性能不佳的表达式

性能、兼容性与脚本稳健性

正则效率

并不是所有正则都一样快。过于模糊、带有嵌套量词和复杂分支的表达式,可能导致严重回溯问题。

例如这类模式需要谨慎:

(a|b*)+

在大文件上,模糊模式会比明确模式慢很多。经验上应优先遵循:

  • 约束范围越明确越好
  • 减少不必要的 .*
  • 避免多层嵌套量词
  • 已知固定字符串时,优先使用固定匹配

实时日志场景

grep 处于管道中,例如:

tail -f logfile | grep 'ERROR'

如果需要实时逐行输出,可以加上:

tail -f logfile | grep --line-buffered 'ERROR'

--line-buffered 会按行刷新输出,避免缓冲区导致延迟。

--mmap

某些系统上可使用:

grep --mmap "pattern" bigfile.txt

它通过内存映射方式读文件,在超大文件上有时更快。但该选项并非所有环境都适合,使用前需要基于目标系统测试。

GNU grep 与 BSD grep 差异

Linux 通常使用 GNU grep,macOS 默认通常是 BSD grep。两者差异包括:

项目 GNU grep BSD grep
-P 支持 通常支持 默认通常不支持
扩展选项 更丰富 相对保守
脚本兼容性 Linux 常见 macOS 常见

脚本如需跨平台,应提前验证:

  • 是否依赖 -P
  • 是否依赖 GNU 特有长选项
  • 字符类、转义、行为细节是否一致

搜索压缩文件:zgrep

日志常以 .gz 压缩存储。无需先解压,可直接搜索:

zgrep "ERROR" /var/log/syslog.2.gz

安全处理文件名:-Zxargs -0

如果要把 grep -l 找到的文件名继续传给其他命令,文件名中的空格会造成问题。更稳妥的做法是空字符分隔:

grep -lZ "pattern" /path/* | xargs -0 rm

这里:

  • -l 只输出文件名
  • -Z / --null 用空字符而非换行分隔
  • xargs -0 按空字符读取

为标准输入命名:--label

当输入来自管道时,输出来源一般标记为标准输入。使用 --label 可以给来源起一个更明确的名字:

echo "This is an error" | grep --label="ErrorStream" "error"

输出:

ErrorStream: This is an error

这对脚本日志和自动化输出整理很有帮助。

常见实战场景

校验 CSV 字段数量

筛选恰好包含 5 个逗号分隔字段的行:

grep -E "^[^,]+,[^,]+,[^,]+,[^,]+,[^,]+$" yourfile.csv

按错误级别筛选日志

grep "ERROR" logs.txt

递归搜索源码中的函数名

grep -r "calculateTotal" /path/to/source/code/directory

匹配 URL

grep -E "https?://[^ ]+" yourfile.txt

过滤停用词行

移除包含 theanda 的行:

grep -vE "the|and|a" yourfile.txt

检测重复字符,辅助发现拼写异常

grep -E "(\w)\1" yourfile.txt

这能匹配包含相邻重复字符的内容。

匹配固定短语

grep -E "named entity recognition" yourfile.txt

CI/CD 日志降噪

先找错误,再排除常见废弃提示:

grep "ERROR" build.log | grep -v "DEPRECATED"

systemd 服务日志排查

journalctl -u nginx.service | grep -i "failed"

代码库中的敏感信息初筛

grep -r -i "API_KEY" .

这不是专用密钥扫描器的替代品,但适合作为快速首检。

grep 在 AI 工作流中的位置

grep 并不理解语义,也不知道函数作用域、类型系统或业务概念,但它在 AI 与数据处理流程里仍然有不可替代的位置。

AI 辅助编写和解释正则

复杂正则的编写和阅读成本一直很高。当前常见的大模型工具适合承担两类工作:

  • 根据自然语言需求生成正则
  • 逐段解释已有正则的结构和含义

例如某个用户名规则需要:

  • 长度 8 到 16
  • 以字母开头
  • 至少包含一个数字
  • 允许下划线但不能在首尾

这种规则直接手写正则,容易出错。用自然语言描述规则,再让模型生成兼容 grep 语法的版本,效率更高。对于遗留脚本中的复杂表达式,也可以先让模型拆解,再回到命令行验证。

在 AI/ML 数据预处理中的作用

大规模训练数据通常很脏。网页抓取文本里可能混入:

  • HTML 片段
  • 非法 JSON
  • 编码异常行
  • 完全无关的噪声文本

在这种阶段,grep 非常适合做首层高速过滤,例如:

  • 提取目标字段:grep '"text":'
  • 去除污染数据:grep -v "<!DOCTYPE html>"
  • 聚焦特定语料:grep -E "\b(error|failed|exception)\b"

在把几十 GB 原始文本送入 Python、Spark 或训练前处理程序之前,先做一轮 grep 过滤,通常能减少资源消耗,也能降低后续程序因坏数据中断的概率。

grep 与 AI 检索工具的边界

两类工具解决的问题不同。

能力维度 grep AI 驱动工具
搜索方式 字符串与模式匹配 语义、上下文、概念检索
结构理解 通常具备代码结构或语义理解
速度 极快 通常较慢
依赖 基本系统自带 需要安装、索引、服务或联网
适合问题 “这段文本是否存在” “这个概念在哪里实现”

适用边界很清晰:

  • 查找已知函数名、错误关键字、固定配置项,grep 更直接
  • 查找“密码重置相关逻辑”“某个领域概念的实现位置”,AI 工具更合适

常见错误与调试

1. 忘记区分 BRE、ERE、PCRE

很多“正则不生效”并不是模式错了,而是运行引擎不对。

例如:

  • +
  • ?
  • |
  • ()

这些在 ERE 中可以直接使用,但在 BRE 中行为不同,通常需要转义或换成 -E

匹配字面量星号:

grep -E "a\*" yourfile.txt

2. 忘记转义特殊字符

如果需要匹配字面量:

  • *
  • +
  • ?
  • .
  • (
  • )

需要根据当前正则方言正确转义。

3. 匹配空行或只包含空白的行

grep -E "^\s*$" yourfile.txt

这个写法依赖具体实现对 \s 的支持。若追求更稳妥的 POSIX 风格,可优先考虑字符类写法。

4. 匹配制表符和回车符

grep -E "\t" yourfile.txt
grep -E "\r" yourfile.txt

不同 shell、不同 grep 实现对转义序列支持可能不同。遇到行为异常时,需要先确认:

  • shell 是否先处理了反斜杠
  • 当前 grep 是否支持该转义
  • 文件里是否真有对应控制字符

5. 引号使用不当

模式里如果包含空格、括号、管道、星号等,通常应整体放进引号中,避免 shell 提前展开。

grepegrepfgrep 的区别

命令 描述 特性 适用场景 示例
grep 基础模式匹配 支持基础正则 通用文本搜索 grep "pattern" file.txt
egrep 扩展模式匹配 支持扩展正则 更复杂的规则匹配 egrep "pattern" file.txt
fgrep 固定字符串匹配 不使用正则 搜索纯文本字符串 fgrep "pattern" file.txt

现代环境中,更常见的写法是:

  • grep -E 代替 egrep
  • grep -F 代替 fgrep

其中 -F 在明确只查固定字符串时通常更快,也更安全,因为不会把模式解释成正则。

多行匹配的限制

grep 是按行处理的工具,不适合天然跨行的模式。

如果需要跨多行匹配,通常应改用 awkperl

awk

awk '/pattern/ {print $0}' yourfile.txt

perl

perl -0777 -ne 'print if /pattern/s' yourfile.txt

这里:

  • -0777perl 一次读取整个文件
  • /s 允许点号跨行匹配

处理多行结构化文本时,直接勉强使用 grep 往往会让表达式更难维护。

常见问答

grepegrep 的区别是什么

grep 默认使用基础正则,egrep 使用扩展正则。现代写法通常用 grep -E 代替 egrep

能否跨多个文件搜索

可以:

grep "pattern" file1.txt file2.txt
grep "pattern" *.txt

如何搜索不匹配某模式的行

grep -v "pattern" yourfile.txt

如何显示匹配结果的行号

grep -n "pattern" yourfile.txt

正则看起来没问题,但命令没按预期工作,优先检查什么

优先检查四项:

  • 当前是否需要 -E-P
  • 特殊字符是否正确转义
  • 模式是否被 shell 提前解释
  • 当前系统是 GNU grep 还是 BSD grep

如何搜索包含空格或特殊字符的模式

把模式放入引号,并按需要转义特殊字符:

grep "pattern\ with\ whitespace" yourfile.txt
grep "pattern\ with\ special\ characters" yourfile.txt

结语

grep 的价值从来不是语法本身,而是它在命令行生态中的位置:足够快、足够稳定、足够容易组合。基础匹配适合直接定位内容,ERE 适合日常规则表达,PCRE 适合精细提取,但也带来兼容性成本。处理日志、源码、配置、压缩文件和训练前语料时,grep 仍然是最可靠的第一层筛选工具之一。

关于

关注我获取更多资讯

月球基地博客公众号二维码,扫码关注获取更多 AI 与编程资讯
📢 公众号
月球基地博客作者个人微信二维码,扫码交流 AI 与编程话题
💬 个人号
使用 Hugo 构建
主题 StackJimmy 设计