如何在 Linux 上使用 grep 命令

Linux 的 grep 命令是一种强大的文本搜索工具,可以从多个文件中查找匹配的行。它还可以与其它命令的输出结果配合使用。下面我们将详细介绍其使用方法。

grep 命令的由来

grep 命令在 Linux 和 Unix 世界中都享有盛誉,这主要有三个原因。首先,它非常实用;其次,它拥有 大量的功能选项,有时甚至让人眼花缭乱;第三,它最初是为了满足特定需求而匆忙编写的。前两个原因广受欢迎,但第三个原因却有些不寻常。

肯·汤普森 从 ed 编辑器中提取了正则表达式搜索的功能(发音为 ee-dee),并创建了一个小型程序供自己使用,用于在文本文件中搜索。他的部门主管,来自 贝尔实验室道格·麦克罗伊,找到了汤普森,并描述了他的一位同事 李·麦克马洪 所面临的问题。

麦克马洪正尝试通过文本分析来确定 《联邦党人文集》 的作者。他需要一种工具,能在文本文件中搜索短语和字符串。 汤普森当天晚上花了一个小时左右的时间,将他自己的工具改造成一个通用的、其他人也可以使用的实用程序,并将其重新命名为 grep。这个名字来源于 ed 命令字符串 g/re/p,意思是“全局正则表达式搜索”。

你可以在 这段视频中看到汤普森和 布莱恩·克尼汉 讨论 grep 的起源。

grep 的基础搜索

要在文件中搜索特定字符串,只需在命令行中输入搜索词和文件名即可:

这样会显示匹配的行。在这个例子中,只有一行匹配。匹配的文本会被高亮显示,这主要是因为在大多数发行版中,grep 都被设置了别名:

alias grep='grep --colour=auto'

接下来,我们来尝试搜索多行匹配的情况。我们将在一个应用程序日志文件中搜索 “Average” 这个词。由于不确定日志文件中这个单词的大小写,我们使用了 -i 选项 (忽略大小写):

grep -i Average geek-1.log

输出结果是每一行匹配的文本都被高亮显示。

我们还可以使用 -v 选项(反向匹配)来显示不包含特定字符串的行。

grep -v Mem geek-1.log

由于这些是不匹配的行,所以没有高亮显示。

我们可以让 grep 完全静默运行。结果会作为 grep 的返回值传递给 shell。返回值为零表示找到了字符串,返回值为一表示未找到。我们可以使用 $? 特殊参数 来检查返回值:

grep -q average geek-1.log
echo $?
grep -q wdzwdz geek-1.log
echo $?

grep 的递归搜索

要搜索嵌套目录及其子目录,可以使用 -r (递归) 选项。请注意,此时不能直接提供文件名,而要提供路径。这里我们在当前目录 “.” 及其所有子目录中进行搜索:

grep -r -i memfree .

输出结果包括每个匹配行所在的文件路径和文件名。

我们可以使用 -R 选项 (递归取消引用) 来让 grep 搜索符号链接所指向的目录。假设当前目录中有一个名为 “logs-folder” 的符号链接,它指向 /home/dave/logs:

ls -l logs-folder

我们再次使用 -R 选项进行搜索:

grep -R -i memfree .

grep 不仅会搜索当前目录,也会搜索符号链接指向的目录。

搜索完整单词

默认情况下,如果搜索的字符串出现在一行中的任意位置,即使它是另一个字符串的一部分,grep 也会匹配该行。例如,我们搜索 “free” 这个词:

grep -i free geek-1.log

结果中包含了包含字符串 “free” 的行,但是这些 “free” 并非独立的单词,而是字符串 “MemFree” 的一部分。

要强制 grep 只匹配完整的 “单词”,可以使用 -w (单词正则表达式) 选项。

grep -w -i free geek-1.log
echo $?

这次没有任何输出,因为 “free” 这个单词没有以单独的完整单词形式出现。

使用多个搜索词

-E (扩展正则表达式) 选项允许搜索多个单词。(-E 选项 取代了已弃用的 egrep 命令)。

下面的命令会搜索 “average” 和 “memfree” 两个单词:

grep -E -w -i "average|memfree" geek-1.log

输出结果显示了包含这两个搜索词的所有匹配行。

你也可以搜索多个不一定是完整单词的短语,也可以是完整单词。

-e 选项 (模式) 允许你在命令行中使用多个搜索词。这里我们使用了正则表达式的括号功能来创建一个搜索模式。它告诉 grep 匹配括号中的任意一个字符 “[ ]”。这意味着 grep 在搜索时会匹配 “kB” 或 “KB”。

两个字符串都被匹配,实际上有些行同时包含两个字符串。

精确匹配行

-x (行正则表达式) 选项只会匹配整行与搜索词完全一致的行。比如,我们搜索日志文件中只出现一次的日期和时间戳:

grep -x "20-Jan--06 15:24:35" geek-1.log

这样会找到并显示匹配的唯一一行。

与此相反的是,仅显示不匹配的行。这在查看配置文件时非常有用。注释固然很好,但有时很难找到实际的设置。以下是 /etc/sudoers 文件:

我们可以使用以下命令有效地过滤掉注释行:

sudo grep -v "https://www.wdzwdz.com/496056/how-to-use-the-grep-command-on-linux/#" /etc/sudoers

这样更容易阅读。

仅显示匹配的文本

有时你可能不想看到整个匹配行,而只想看到匹配的文本。-o (仅匹配) 选项可以满足这个需求。

grep -o MemFree geek-1.log

结果减少到只显示与搜索词匹配的文本,而不是整个匹配行。

使用 grep 计数

grep 不仅可以处理文本,还可以提供数字信息。 我们可以用不同的方式使用 grep 进行计数。如果我们想知道一个搜索词在一个文件中出现了多少次,可以使用 -c (计数) 选项。

grep -c average geek-1.log

grep 报告该搜索词在该文件中出现了 240 次。

您可以使用 -n (行号) 选项让 grep 显示每个匹配行的行号。

grep -n Jan geek-1.log

每个匹配行的行号都会显示在该行的开头。

要限制显示的匹配行数,可以使用 -m (最大计数) 选项。比如,我们将输出限制为前五个匹配行:

grep -m5 -n Jan geek-1.log

添加上下文

对于每个匹配行,能够看到一些额外的行(可能是不匹配的行) 通常很有用。这有助于区分哪些匹配行是您感兴趣的。

要在匹配行之后显示几行,可以使用 -A (上下文之后) 选项。在这个例子中,我们要求显示三行:

grep -A 3 -x "20-Jan-06 15:24:35" geek-1.log

要查看匹配行之前的几行,请使用 -B (之前的上下文) 选项。

grep -B 3 -x "20-Jan-06 15:24:35" geek-1.log

如果想包含匹配行之前和之后的行,请使用 -C (上下文) 选项。

grep -C 3 -x "20-Jan-06 15:24:35" geek-1.log

显示匹配文件

要查看包含搜索词的文件名,请使用 -l (匹配的文件) 选项。 例如,要查找哪些 C 源代码文件包含对 sl.h 头文件的引用,可以使用以下命令:

grep -l "sl.h" *.c

输出结果会列出文件名,而不是匹配的行。

当然,我们也可以查找不包含搜索词的文件。-L (不匹配的文件) 选项就是为此而设计的。

grep -L "sl.h" *.c

行的开始和结束

我们可以强制 grep 只显示位于行首或行尾的匹配项。”^” 正则表达式运算符匹配行首。实际上,日志文件中的所有行都包含空格,但我们搜索以空格开头的行:

grep "^ " geek-1.log

这样会显示以空格作为第一个字符的行。

要匹配行尾,请使用 “$” 正则表达式运算符。我们搜索以 “00” 结尾的行:

grep "00$" geek-1.log

显示的结果是以 “00” 作为最后一个字符的行。

grep 的管道使用

grep 可以方便地与其他命令进行管道连接,将 grep 的输出传递给另一个程序,或者将 grep 放在管道链的中间。

假设我们想在 C 源代码文件中查看所有出现的字符串 “ExtractParameters”。我们知道这会产生很多输出,所以我们将结果通过管道传递给 less:

grep "ExtractParameters" *.c | less

输出结果将以 less 的形式呈现。

这样你就可以浏览文件列表并使用 less 的搜索功能。

如果将 grep 的输出通过管道传递给 wc 并使用 -l (行) 选项,我们 可以计算行数 在包含 “ExtractParameters” 的源代码文件中。(我们也可以使用 grep -c (计数) 选项来实现,但这可以用来演示 grep 的管道使用方式。)

grep "ExtractParameters" *.c | wc -l

下面的命令中,我们将 ls 的输出传递给 grep,然后将 grep 的输出传递给 sort。我们列出当前目录中的文件,选择带有字符串 “Aug” 的文件,并按照文件大小排序

ls -l | grep "Aug" | sort +4n

让我们分解一下这个命令:

ls -l: 使用 ls 执行文件的长格式列表。
grep “Aug”: 从 ls 列表中选择包含 “Aug” 的行。 请注意,这也会找到文件名中包含 “Aug” 的文件。
sort +4n: 对 grep 输出的第四列(文件大小)进行排序。

我们得到了所有在八月份修改的文件列表,按照文件大小升序排列。

grep:一个强大的盟友

grep 是一个非常实用的工具。它诞生于 1974 年,但直到今天依然强大。它仍然是我们需要的功能的最佳选择。

将 grep 与一些正则表达式结合起来,可以让它的功能更上一层楼。