Grep(Global Regular Expression print)命令是 Unix/Linux 系统中的强大文本搜索工具。Grep 接收诸如正则表达式或字符串之类的模式,然后在一个或多个输入文件中搜索包含预期模式的行。Grep 命令可广泛用于文本搜索与过滤、日志分析、代码扫描、配置管理、数据提取等。在软件开发中,文本搜索用于代码导航、重构、调试、错误诊断、安全威胁扫描、版本控制以及代码审查。文本搜索工具能够显著减少开发人员查找特定函数、变量或错误消息所花费的时间。在系统管理中,文本搜索在日志分析与监控、安全与威胁检测、数据处理和自动化等任务中非常好用。像 grep、awk 和 sed 这样的文本处理工具用于扫描日志,以分析认证事件、特定异常,并通过严重级别、时间戳或关键词对日志进行筛选,帮助管理员发现故障、安全漏洞以及性能问题。在这篇博客中,我们将全面探讨 Grep 的功能、示例与使用场景。通过使用正则表达式匹配来搜索文件和输入中的文本模式,Select-String 也可以在 Windows 中作为 PowerShell 对应于 grep 的用法。
Netwrix Auditor for Active Directory
全面了解您的 Active Directory 中正在发生的情况
基本语法与用法
grep 命令是在 Unix/Linux 中搜索文本模式、用于文本筛选与分析的强大工具。下面是包含模式、文件和选项的基本命令结构。
grep [options] pattern [file…]
- 模式: 要搜索的正则表达式文本。
- 文件: 要在其中进行搜索的文件或文件集。
- 选项: 用于改变 grep 行为的修饰符或开关。选项通常前面带有连字符(-)。
以下是一些最常用的选项。
- -i: 在搜索模式和数据中忽略大小写。例如,下面的命令将搜索“hello”、“HELLO”、“Hello”等。
grep -i “hello” file.txt
- -v: 反转搜索匹配结果,显示与指定模式不匹配的行。例如,下面的命令会显示不包含“hello”的行。此选项有助于查找不符合特定条件的行。
grep -v “hello” file.txt
- -n: 在每一行匹配条件的内容之前显示行号,并有助于报告共享。例如,下面的命令会显示单词“function”出现的位置对应的行号。
grep -n “function” file.txt
- -r: 递归搜索目录,即在目录及其子目录中的所有文件里查找该模式。
- –color: 在输出中突出显示匹配的字符串。例如,下面的命令会在输出中高亮显示 “hello”。
grep –color “hello” file.txt
- -l: 只列出包含至少一个匹配项的文件名。
grep -l “starting” *.log
平台兼容性
Grep 在 Unix/Linux 系统的命令行中默认集成,并且按预期以一致的方式运行。它支持正则表达式、支持管道(piping),并能与其他 Unix/Linux 工具无缝集成。Windows 系统中可通过 Windows subsystem for Linux (WSL) 使用 Grep,从而让用户无需虚拟机的开销即可在 windows 上直接运行 GNU/Linux 环境。此外,windows 还提供了多种原生版本的 grep,这些都是编译后的独立版本,可直接在 windows 上运行,例如 Git Bash、Gnuwin32。
虽然 grep 的设计目标是在各个平台上保持一致,但在不同平台上使用时,需要注意一些差异和限制。
- 行结束符: Unix/Linux 系统使用 ‘\n’ 作为行结束符,而 windows 使用 ‘\r\n’。
- 路径表示: 文件系统在 Unix/Linux 与 windows 之间的行为不同。windows 路径使用反斜杠 ‘\’,而不是 Unix/Linux 中使用的 ‘/’。
- 字符编码: 不同平台使用不同的默认字符编码,尤其是在处理非 ASCII 文本时。
- 命令行选项: 大多数常见的 grep 选项在各个平台上都受支持。但在不同平台上,grep 的支持可能会有限,例如在 Windows 上的管道(piping)支持可能较为有限。
grep 实战示例
简单的文本搜索
在下面的示例中,我们在日志文件中搜索字符串“deployment”。
Grep “deployment” logemail.log
在下面的示例中,我们使用选项 -i 搜索字符串“starting”,该选项会忽略大小写差异。
grep -i “starting” logemail.log
而如果我们不使用 -i 选项,则会在整个文件中匹配精确的字符串,例如 grep “Starting” logemail.log 这条命令会搜索 Starting,并忽略诸如“starting”或“STARTING”之类的匹配项,或任何其他对字符串“starting”大小写敏感的组合。
递归搜索
有时文件分散在不同的目录中,需要在多个文件和目录上运行模式搜索。使用 -r 选项并结合 --include 和 --exclude 进行 grep 递归搜索,可以提供一个快速的解决方案。在下面的命令中,我们会在当前目录及其子目录中的所有 .log 文件里递归搜索模式“starting”,并且只打印匹配到该模式的日志文件中的第一条记录。我们当前位于“Documents”目录,其中包含子目录“office”和“project”。
grep -r “starting” –include=”*.log” -m1 –color=always
在下面的示例中,我们排除所有日志文件,并在目录“Documents”及其子目录中的所有文件里递归搜索字符串“starting”。
Grep -r “starting” –exclude=”*.log”
反转匹配结果
我们可以使用 -v 选项来反转搜索结果。这样,我们就可以搜索某个字符串,并找到所有不包含该字符串的行。下面的示例中,我们使用选项 -v 查找不包含“starting”的所有行,也就是查找搜索字符串“starting”对应的反向结果。
Grep -v “starting” logmail.log
行号与上下文输出:
在文件中查找模式时,查看包含该搜索模式的精确行号很有用;有时,将搜索匹配项周围的上下文一并呈现会更好。比如在浏览日志文件以查找异常时,最好在搜索字符串前后各包含一些行,让它们出现在搜索结果中。下面的示例中,我们使用 -n 选项来打印与匹配模式对应的行号。
Grep -n “starting” logemail.log
使用选项 -A,我们可以打印匹配结果之后的行;使用选项 -B,可以打印匹配结果之前的几行;使用 -C,则可以在搜索结果的前后各打印一些行。在下面的示例中,我们使用 -A、-B 和 -C 来显示搜索结果前后的行。
grep -A 2 “starting” logemail.log
grep -B 2 “starting” logemail.log
Grep -C 1 “starting” logemail.log
在 grep 中使用正则表达式
正则表达式是用于定义搜索模式的一串字符,常用于字符串匹配与处理。下面是一些基本的正则表达式。
- 点号(.): 匹配除换行符以外的任意单个字符。即: “c.t” 可匹配 cat、cot、crt、cet 等。
- 星号(*): 匹配前一个字符出现 0 次或多次。即: “c*t” 可匹配 ct、cat、caat、caaaat 等。
- 插入符(^): 匹配开头位置。也就是说:^an 只有在位于行首时才会与 an 匹配。
- 美元符号($): 匹配行尾位置。也就是说:$finished 只有在位于行尾时才会与 finished 匹配。
- 竖线(|): 竖线在正则表达式中相当于逻辑 OR。也就是说:(apple | banana) 会匹配该行中的 apple 或 banana。
- 转义字符 (\): 用于转义特殊字符,例如 \ 。匹配字面量的句点。
Grep 支持正则表达式;它使用基本正则表达式(BRE),同时也支持使用 -E 标志的扩展正则表达式(ERE)以及使用 -P 标志的 Perl 兼容正则表达式(PCERE)。扩展正则表达式提供更多元字符,例如 +(一个或多个匹配)、?(零次或一次匹配)、|(逻辑 OR)、{}(分组模式),从而进行更高级的模式搜索。Perl 兼容正则表达式功能最强且最灵活,提供更多选项,例如前瞻 (?=)、后顾 (?<!)、非捕获组 (?:pattern) 等。
在下面的示例中,我们会在文件中查找完整单词,并使用字符串匹配来展示 grep 命令的变体。
- Grep “end” log.txt,将匹配 end 这个单词的所有可能变体。
- grep -w “end” log.txt,将只匹配整个单词 “end”。
- grep “\bend\b” log.txt, 将仅使用正则表达式匹配整个单词“end”。
- Grep “\bend” log.txt, 会匹配行首的字符串“end”。
在下面的示例中,我们使用不同的变体来匹配文件“log.txt”中的数字。
- grep “[0-9]” log.txt, 将查找包含任意数字的所有行。
- grep “[0-9]\{3\}-[0-9]\{3\}-[0-9]\{4\}” log.txt , 将在指定文件中查找电话号码。
- grep -E “[0-9]{2,4}” log.txt , 将查找包含连续数字 2 位、3 位或 4 位的行。
在下面的示例中,我们会在文件 log.txt 中查找空白(whitespace)。
- grep “^[[:space:]]” log.txt 将查找行开头的空格。
- grep “^[[:space:]]” log.txt 将查找行末的空格。
使用 grep 命令可以查找诸如 IP 地址和电子邮件之类的复杂模式。在下面的示例中,我们使用正则表达式来查找 IP 地址。
grep -E “\(?[0-9]{3}\)?[-. ]?[0-9]{3}[-. ]?[0-9]{4}” log.txt
在下面的示例中,我们使用正则表达式来查找电子邮件地址。
grep -E “[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}” log.txt
高级正则表达式技巧(例如,使用 -P 的前向查看和后向查看)
前向查看(lookahead)和后向查看(lookbehind)是一种强大的技术,可根据其周围的模式来查找模式。例如,如果我们想在日志文件中搜索“error”,但前提是该行中也有应用程序正在启动的信息,则只能在这种情况下进行搜索。前向查看有两种类型:正向查看(Positive lookahead)和负向查看(Negative lookahead)。
- 正向前瞻(Positive lookahead,?=…)确保括号内的模式出现在当前位置之后,但不会将当前位置包含在匹配结果中。也就是说,如果日志行中“starting”紧跟在“error”后面,我们就可以搜索“error”。
- 负向前瞻(Negative lookahead,(?!…)确保括号内的模式不会出现在当前位置之后。也就是说,我们可以搜索模式“starting”,但不能是后面紧跟模式“error”的情况。
grep -P “error(?= starting)” log.txt
grep -P “starting(?!= error)” log.txt
lookbehind 有两种类型:正向后顾(Positive lookbehind)和负向后顾(Negative lookbehind)。
- 正向后顾 (?<=…),确保括号内的模式出现在当前的位置之前,但不包含在匹配结果中。
- 负向后顾 (?<!…),确保括号内的模式不出现在当前的位置之前。
Grep -P “(?<=starting )error” log.txt
grep -P “error)?=.*starting)” log.txt
grep 的高级功能
组合多个模式
grep 命令也允许我们将多个模式用于搜索;可以使用 -e 选项来组合多个模式。在下面的示例中,我们在同一个文件中搜索两种不同的模式。
Grep -e “starting” -e “error” log.txt
此外,我们还可以使用 -e 在多个文件中查找多个模式:例如在 log.txt 文件中查找“starting”,在 logemail.log 中查找“error”。
grep -e “starting” -e “error” log.txt logemail.log
输出自定义
您可以使用 –color 选项在输出中突出显示搜索到的模式,无论是在控制台打印还是将输出重定向到文件。
- –color=auto 会根据输出是否将发送到终端来决定是否使用颜色
- –color=always:始终使用颜色,即使输出正在重定向到文件。
- –color=never:永不使用颜色。
grep –color=auto “error” log.txt
grep –color=always “error” log.txt
grep –color=never “error” log.txt
我们可以使用 -q 选项来修改 grep 命令的输出,使其安静地运行。使用 -q 选项不会打印所有匹配的行,只会输出自定义消息。在下面的示例中,我们在 log.txt 文件中查找模式 “error”,并打印 “error found”。
grep -q “error” log.txt && echo “error found!” || echo “No error found”
if grep -qi “error” log.txt; then echo ” error found”; fi
性能优化
grep 读取大文件时不会将文件全部加载到内存中;不过,我们可以通过一些额外的技巧进一步提升其性能。
- 过度使用正则表达式: 正则表达式(Regex)计算开销较大。在需要查找精确的字面字符串时,可以使用固定字符串的 grep,避免正则计算带来的额外开销。
- 使用 –mmap: 如果在文件内部进行大量随机访问,可以使用 –mmap 来启用内存映射文件访问。
- 并行处理: 如果我们的任务允许将大文件拆分,并在不同部分上运行多个 grep 进程,那么这有助于性能优化:它会运行多个 grep 进程实例,之后我们可以合并结果。
- 限制输出: 我们可以限制输出,只显示搜索到的第一个或第二个匹配项;或者抑制输出,只检查模式是否存在。
Grep 命令默认会对输出进行缓冲,这可能会在通过管道将命令串联时延迟实时处理。使用 “–line-buffered” 选项可以强制在每次匹配时立即输出。下面的示例中,我们将 tail 和 grep 进行串联,以持续监控日志文件,并按行输出匹配的模式 “error”。
Tail -f log.txt | grep –line-buffered “error”
基于文件的模式匹配
我们可以用 grep 命令创建一个用于搜索的模式文件,然后使用 –file 选项从文件中搜索多个模式。在下面的示例中,我们创建了一个包含模式“starting”、“application”和“INFO”的文件 pattern.txt,并在 grep 命令中使用 –file 来在文件 logemail.log 中搜索这些模式;同时使用 -m2 选项,以显示两次出现的结果。
grep –file=pattern.txt -m2 logemai.log
grep 的管道和重定向
我们可以将 grep 命令与其他命令串联,以适应不同的场景。在下面的命令中,我们使用进程状态(ps)命令获取所有进程,并将结果通过管道传递给 grep,只输出 python 进程。
ps aux | grep python
在下面的示例中,我们会获取当前目录中的所有文件和文件夹,然后使用 grep 命令仅筛选日志文件。
ls -a | grep ‘\.log$’
在下面的示例中,我们使用 ps、grep 和 awk 命令来打印正在使用 Python 进程的用户名称。
ps aux | grep python | awk ‘{print $1}’
在下面的示例中,我们会在 erros.txt 文件中查找模式“error”,并使用 sed 命令将所有“error”出现的位置突出显示为“Alert”。
grep “error” erros.txt | sed ‘s/error/ALERT/’
在以下示例中,我们会在文件 log.txt 中搜索模式“error”,并将输出重定向到当前目录中的另一个文件 erros.txt。第一个命令会覆盖 errors.txt 文件中的输出;第二个命令会将 grep 在 error.txt 文件中的输出追加写入。
grep “error” log.txt > errors.txt
grep “error” logemail.log >> errors.txt
在下面的命令中,我们使用 tee 将 grep 的输出写入到 erros.txt 文件,同时也在控制台上打印输出。
grep “starting” logemail.log | tee errors.txt
真实世界的用例与示例
日志文件分析与过滤
(如上已给出大量示例)
从数据文件中进行文本处理与提取
(如上提供了很多示例)
我们可以使用 netstat 和 ss 命令获取不同端口的状态,以及正在监听这些端口的进程。结合 grep 进一步缩小范围,也可以筛选到特定端口。下面的示例中,我们使用 netstat 和 ss 命令来获取正在监听不同端口的所有进程。
netstat -lntp | grep “LISTEN”
ss -lntp | grep “LISTEN”
我们可以将 grep 命令与其他命令结合使用,以获取系统信息并快速搜索不同的配置项。下面的示例中,我们将检查不同的系统信息,这对诊断用途很有帮助。
ps aux | grep “CPU” # 用于检查 CPU 统计信息。
df -h | grep “/dev/sd” # 用于检查磁盘使用情况。
ip a | grep “inet” # 用于查找 IP 地址
自定义并为 grep 设置别名
我们可以为带有不同选项的 grep 命令定义别名,然后在搜索模式时使用这些别名,而不是每次都使用带选项的 grep 命令。
alias g=’grep’
alias gi=’grep -i’
alias gr=’grep -r’
alias gc=’gr -n –color=auto’
定义这些别名之后,我们就可以仅使用别名来进行模式搜索。
g “error” -m1 log.txt
gi “error” -m1 log.txt
gr “error” -m1 log.txt
gc “error” -m1 log.txt
我们可以编写一个函数来读取前 10 条系统日志。在下面的示例中,我们编写了一个函数 “find_error”,用于读取位置为 “/var/log/systemlog” 的 syslog 文件,并输出包含模式 “error” 的最后 10 行。
find_errors{
grep -I “error” /var/log/syslog | tail -n 10
}
Find_errors
我们使用 tail、grep 和 tee 命令来搜索 syslog,并使用“error”关键字筛选错误;然后在控制台上显示输出,并将其添加到日志文件中。
tail -f /var/log/syslog | grep –line-buffered -i “error” | tee errors.txt
与 Shell 脚本的集成(使用 grep 结合条件语句和循环)
在 shell 脚本中使用 grep 实现自动化的示例
(本节内容更复杂,需要在 Linux 上配置更多的虚拟机和 Windows。没有时间分享示例,我建议将 H2 更改为“Using grep with conditional statements and loops”)
在下面的示例中,我们使用 grep -i 搜索特定模式,并将输出通过管道传递给 while 循环,随后处理每一行匹配的结果。
#!/bin/bash
LOG_FILE=”/var/log/syslog”
PATTERN=”authentication”
grep -i “$PATTERN” “$LOG_FILE” | while read -r line; do
echo “Processing line: $line”
# 在此处执行额外处理
完成
在下面的示例中,我们使用带有 ps 和 grep 命令的条件语句来检查服务是正在运行还是未运行。
#!/bin/bash
SERVICE=”CUPS”
if ! ps aux | grep -v grep | grep -q “$SERVICE”; then
echo ” $SERVICE is not running!”
else
echo “$SERVICE is running.”
fi
故障排查与常见误区
解决编码问题
编码不匹配可能会导致在搜索不同编码字符中的模式时,grep 命令失败。我们可以设置区域设置(LC_ALL=C),或使用 –encoding 选项来纠正编码问题。
处理特殊字符与转义
正则表达式使用需要进行转义的特殊字符,才能按字面含义使用。反斜杠(\)用于转义这些字符;或者可以使用 -F(固定字符串)选项,将模式当作字面字符串处理。
调试复杂的正则表达式模式
当复杂的正则表达式未能按预期场景返回结果时,有时会很难排查。将其拆分成小部分,逐个测试,然后再组合起来,可以节省时间并帮助你定位问题。
结论
我们已经全面介绍了 grep 命令,从基础功能到诸如正则表达式这样的进阶技巧、grep 命令的不同选项、将 grep 与其他命令进行管道连接、重定向输出、脚本编写以及排错技巧。就像任何其他 PowerShell 技巧一样,通过亲自动手实践并对 grep 命令进行尝试,可以提高理解力,并帮助你发掘掌握系统自动化的隐藏可能性。
分享到
了解更多
关于作者
Tyler Reese
产品管理副总裁,CISSP
凭借在软件安全行业超过二十年的经验,Tyler Reese 对当今企业面临的快速演变的身份与安全挑战非常熟悉。目前,他担任 Netwrix Identity and Access Management 组合的产品总监;他的职责包括评估市场趋势、确定 IAM 产品线的发展方向,并最终满足终端用户的需求。他的职业经历从为财富 500 强公司提供 IAM 咨询,到在一家大型直销面向消费者(direct-to-consumer)的公司担任企业架构师,涵盖范围十分广泛。 目前,他持有 CISSP 认证。