Contents

Grep、sed和awk的区别

1. 概述

在本文中,我们将介绍命令行工具grepsedawk 。特别是,我们将研究它们之间的功能差异。

2. 背景

当谈到 Linux 中的文本处理时,非常方便的三个工具是grepsedawk。尽管是完全不同的工具,但它们的功能似乎在简单的场景中重叠。例如,要在文件中查找模式并将匹配项打印到标准输出,我们会发现它们都可以做到这一点。

但是,如果我们超越这个简单的练习,我们会发现**grep 只适用于简单的文本匹配和打印**。

另一方面,除了匹配和打印文本之外,sed 还提供了额外的文本转换命令,例如替换

最后,作为这些工具中最强大的awk 是一种脚本语言,它提供了前两者中不存在的众多功能。

在开始之前,重要的是要知道**本文的目的是让这三个工具之间的区别更加清晰。**因此,我们所涵盖的示例只是每个工具可能实现的一小部分,尤其是在sedawk 的情况下。

3. 文本文件

为了方便我们的讨论,让我们定义一个文本文件 log.txt

Timestamp       Category        Message
1598843202      INFO    Booting up system
1598843402      INFO    Booting up critical service: Authorization
1598843502      INFO    System booted successfully
1598853502      INFO    User admin requested access for userlist
1598863888      ERROR   User annonymous attempt to access protected resource without credentials
1598863891      INFO    System health check status: passed
1598863901      ERROR   Requested resource not found
1598864411      INFO    User admin logged out

4. grep

** grep命令搜索匹配正则表达式模式的行并将这些匹配的行打印到标准输出**。当我们需要一种快速的方法来找出给定输入中是否存在特定模式时,它很有用。

4.1. 基本语法

grep的语法如下:

grep [OPTIONS] PATTERN [FILE...]

** PATTERN是一个正则表达式模式,它定义了我们想要在FILE参数指定的文件内容中找到的内容**。 OPTIONS可选参数是修改grep行为的 。

4.2. 搜索匹配正则表达式模式的行

假设我们 要从log.txt中提取ERROR事件。我们可以用grep做到这一点:

$ grep "ERROR" log.txt
1598863888	ERROR	User annonymous attempt to access protected resource without credentials
1598863901	ERROR	Requested resource not found

这里发生的是grep将扫描log.txt中的行并将那些包含单词ERROR的行打印到标准输出。

4.3. 反转匹配

我们可以使用*-v*标志反转匹配:

grep -v "INFO" log.txt

当我们执行上面的命令时,** grep将打印log.txt中的每一行,除了那些匹配模式INFO 的行。**

4.4. 打印前行或后行

有时,我们可能希望在匹配项周围打印前一行或后一行。要在匹配后打印五行,我们可以使用标志*-A*:

grep -A 5 ERROR log.txt

另一方面,要在比赛前打印五行,我们可以使用标志*-B*:

grep -B 5 ERROR log.txt

最后,标志*-C*允许我们打印匹配前的五行和匹配后的五行:

grep -C 5 ERROR log.txt

5. sed

sed命令是一个处理字符流的流编辑器。它是一个比grep更强大的工具,因为它提供了更多用于文本处理目的的选项,包括sed最常见的替换命令。

5.1. 基本语法

sed命令具有以下一般语法:

sed [OPTIONS] SCRIPT FILE...

OPTIONS是可选标志, 可应用于sed以修改其行为。接下来,SCRIPT参数是sed脚本,它将在 FILE参数指定的文件的每一行上执行。

5.2. 脚本结构

sed脚本具有以下结构:

[addr]X[options]

其中addr是应用于文本文件行的条件。它可以是固定数字或正则表达式模式,在处理之前针对行的内容进行测试。

接下来,X字符代表要执行的sed命令。例如,用单个字符表示的替换命令。

最后,可以将其他选项传递给sed命令以指定其行为。

5.3. 使用sed作为grep

作为初学者,让我们看看如何使用sed复制grep的功能:

sed -n '/ERROR/ p' log.txt

默认情况下,sed会将扫描到的每一行打印到标准输出流。要禁用此自动打印,我们可以使用标志*-n*。

接下来,它将运行标志*-n之后的脚本,并在log.txt的每一行中查找正则表达式模式 ERROR。如果匹配,sed将把该行打印到标准输出,因为我们在脚本中使用了p命令。最后,我们将log.txt作为我们希望sed*处理的文件的名称作为最后一个参数传递。

5.4. 用替换替换匹配的字符串

sed的 替代命令具有以下结构:

's/pattern/replacement/'

pattern的一行匹配时,sed将用replacement替换它。

例如,如果我们想用单词CRITICAL替换log.txt中的单词ERROR,我们可以运行:

sed 's/ERROR/CRITICAL/' log.txt

5.5. 就地修改文件

如果我们希望sed将更改保留在它正在操作的文件上,我们可以使用标志*-i*和后缀。在进行就地更改之前,sed将创建文件的备份并将后缀附加到此备份文件名。例如,当我们运行时:

sed -ibackup 's/ERROR/CRITICAL/' log.txt

sed应用更改 之前, log.txt将被复制并重命名为log.txtbackup

5.6. 限制为特定的行号

我们可以限制sed命令,使其仅使用脚本中的addr插槽对特定行号进行操作:

sed '3 s/ERROR/CRITICAL/' log.txt

这将仅在log.txt的第 3 行运行脚本。 此外,我们可以指定一系列行号:

sed '3,5 s/ERROR/CRITICAL/' log.txt

在这种情况下,sed将在log.txt的第 3 到 5 行运行脚本。

此外,我们可以使用正则表达式模式指定边界:

sed -n '3,/ERROR/ p' log.txt

在这里,sed将从第 3 行开始打印*log.txt的行,并在找到与模式**/ERROR/*匹配的第一行时结束。

6. awk

awk 是一种成熟的编程语言,可与Perl 相媲美。它不仅提供了大量用于字符串、算术和时间操作的内置函数,还允许用户像任何常规脚本语言一样定义自己的函数。让我们看一些它是如何工作的例子。

6.1.基本语法

awk语法具有以下 形式:

awk [options] script file

它将针对文件中的每一行执行脚本。现在让我们扩展脚本的结构:

'(pattern){action}'

pattern是一个正则表达式模式,将针对每个输入行进行测试。如果一行与pattern匹配,则awk将在该行上执行在 action中定义的脚本。如果不存在pattern条件,则将在每一行上执行该action

6.2. 使用 awk复制 grep

正如我们对sed所做的那样,让我们看看如何使用awk模拟grep的功能:

awk '/ERROR/{print $0}' log.txt

上面的代码将在log.txt文件中找到正则表达式模式ERROR并将匹配的行打印到标准输出。

6.3. 替换匹配字符串

类似地,我们可以使用awk的内置方法 gsub将所有出现的ERROR替换为CRITICAL,就像在sed示例中一样:

awk '{gsub(/ERROR/, "CRITICAL")}{print}' log.txt

gsub方法将正则表达式模式和替换字符串作为参数。然后,awk将该行打印到标准输出。

6.4. 向文档添加页眉和页脚

awk中,有一个 BEGIN块将在开始处理文件的任何行之前执行。另一方面,还有一个END块,它允许我们定义在处理完所有行之后应该运行什么。

让我们使用BEGINEND块来为我们的文本文档添加页眉和页脚:

$ awk 'BEGIN {print "LOG SUMMARY\n--------------"} {print} END {print "--------------\nEND OF LOG SUMMARY"}' log.txt
## LOG SUMMARY
Timestamp	Category	Message
1598843202      INFO    Booting up system
1598843402      INFO    Booting up critical service: Authorization
1598843502      INFO    System booted successfully
1598853502      INFO    User admin requested access for userlist
1598863888      ERROR   User annonymous attempt to access protected resource without credentials
1598863891      INFO    System health check status: passed
1598863901      ERROR   Requested resource not found
## 1598864411      INFO    User admin logged out
END OF LOG SUMMARY

6.5. 列操作

处理具有行和列结构(CSV 样式)的文档是awk真正发挥作用的时候。例如,我们可以轻松打印第一列和第二列,并跳过log.txt的第三列:

awk '{print $1, $2}' log.txt

6.6. 自定义字段分隔符

默认情况下,awk将空格作为分隔符处理。如果处理文本使用的分隔符不是空格(例如逗号),我们可以使用标志*-F*指定它:

awk -F "," '{print $1, $2}' log.txt

6.7. 算术运算

awk执行算术运算的能力使得收集有关文本文件的一些数字信息变得容易。例如,让我们计算log.txtERROR事件发生的次数:

awk '{count[$2]++} END {print count["ERROR"]}' log.txt

在上面的脚本中,awk将每个不同值Category列的计数存储在变量count中。然后脚本在最后打印count数值。

6.8. 数值比较

作为一种成熟的脚本语言,awk很容易理解十进制值。当我们需要我们的脚本将值解释为数字而不是简单的字符串时,这使得文本处理变得容易。

例如,假设我们想要获取所有早于时间戳 1598863888 的日志条目,我们可以使用大于比较器:

$ awk '{ if ($1 > 1598863888 ) {print $0} }' log.txt
1598863891      INFO    System health check status: passed
1598863901      ERROR   Requested resource not found
1598864411      INFO      User admin logged ou

从输出中,我们可以看到该命令仅打印晚于指定时间戳记录的日志行。