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 与一些正则表达式结合起来,可以让它的功能更上一层楼。