了解如何使用 AWK 命令

AWK 代表“Aho Weinberg Kernighan”,是发明它的人的姓氏:Alfred Aho、Peter Weinberg 和 Brian Kernighan。 这 AWK的目的 是搜索现有文件以查找匹配特定模式的行。 它是一个完整的脚本语言,也是一个完整的文本操作工具包。 它是数据驱动的,这意味着您定义一组要对提供的文本执行的操作,并将结果发送到标准输出。

使用 AWK,我们可以:

  • 逐行扫描文件。
  • 将每个输入行拆分为字段。
  • 将输入行或字段与模式进行比较。
  • 在匹配的行上执行操作。

模式用斜杠 (//),动作用大括号 ({}) 括起来,整个 AWK 程序用单引号 (‘) 括起来。 awk 命令的默认分隔符是任何空白字符,如空格或制表符。 如果 awk 命令中没有模式,则将匹配提供的文件中的所有行。
让我们用 ls -l 命令查看当前文件夹的内容。

[[email protected] public_html]$ ls -l total 12 -rw-rw-r--. 1 mstevens mstevens 6426 Feb  9 08:00 access_log -rw-rw-r--. 1 mstevens mstevens    0 Mar 19 04:48 config.php -rw-r--r--. 1 mstevens mstevens 3661 Mar 19 04:31 dovecot.log -rw-rw-r--. 1 mstevens mstevens    0 Mar 19 04:48 error_log -rwxrwxrwx. 1 mstevens mstevens    0 Mar 19 04:49 everyone.txt -rw-rw-r--. 1 mstevens mstevens    0 Mar 19 04:48 index.php -rw-rw-r--. 1 mstevens mstevens    0 Mar 19 04:49 list.php -rw-rw-r--. 1 mstevens mstevens    0 Mar 19 04:49 login.php -rw-rw-r--. 1 mstevens mstevens    0 Mar 24 03:14 php.ini

ls 命令的输出显示了块的总数(在本例中为 12)并包含九个字段(从左到右):

  1. 权限
  2. 连接数
  3. 用户
  4. 团体
  5. 尺寸
  6. 上次更新时间
  7. 文档名称

例如,如果我们只需要打印权限和文件名,我们可以将 ls -l 命令通过管道传送到 AWK 并告诉它打印第一个和第九个字段。
下面的简单 AWK 程序没有模式,只有动作,因此它将通过仅显示每行的第一个和第九个字段来检查和匹配提供的每一行文本。

[[email protected] public_html]$ ls -l | awk '{print $1,$9}' total -rw-rw-r--. access_log -rw-rw-r--. config.php -rw-r--r--. dovecot.log -rw-rw-r--. error_log -rwxrwxrwx. everyone.txt -rw-rw-r--. index.php -rw-rw-r--. list.php -rw-rw-r--. login.php -rw-rw-r--. php.ini

如您所见,ls 命令输出有 10 行文本,包括带有单词的行 全部的. 这个单词 全部的 是其行上的第一个字段,数字 12 是其线路上的第二个字段。 仅有的 全部的 在输出中返回,因为 awk 命令请求了第一个和第九个字段。 为了避免匹配不需要的行,我们可以提供一个模式,只有具有该模式的行才会被输出。

模式匹配

AWK 中的模式 用于在与给定模式匹配的行上显示特定操作。 同样的事情可以用 grep 命令来完成,以在提供的文本或文件中查找某些信息。 唯一的区别是我们不需要组合多个命令; 我们只需要使用一个 awk 命令。

AWK 支持不同类型的模式:

  • 正则表达式模式
  • 关系表达模式
  • 范围模式
  • 特殊表达

正则表达式模式

最基本的例子是字符串匹配。 如果我们只想得到带有单词的行 php,我们可以在斜杠 (//) 之间的 awk 命令中添加一个模式。 如下图,无论在哪个词 php 位于行中,这些文件显示在输出中。

[[email protected] public_html]$ ls -l | awk '/php/ {print $1,$9}' -rw-rw-r--. config.php -rw-rw-r--. index.php -rw-rw-r--. list.php -rw-rw-r--. login.php -rw-rw-r--. php.ini

正则表达式语法字符

正则表达式是描述一定数量文本的模式。 为了不要将它与 awk 模式之一的“正则表达式模式”混淆,我将使用在 IT 中也广泛使用的“regex”。

某些字符在正则表达式中使用时具有特殊含义。

锚不匹配任何字符。 相反,它们匹配字符之前或之后的位置。

查看表

功能
^ 表示行的开头。
$ 表示一行的结束。
一种 表示字符串的开头。
表示字符串的结尾。
b 标记单词边界。

人物

您可以匹配遵循特定规则的字符。

查看表

特点 功能
[ae] 选择 一种 或者 电子.
[a-e] 选择从 a 到 e(a、b、c、d 或 e)的任何字符。
[^a-e] 选择任意字符 除了 a 到 e(f、g、h 等)。
w 选择任何单词。
s 选择任何空白字符。
b 选择任何数字。

量词

量词指定输入中必须存在多少个字符、组或字符类的实例才能找到匹配项。

查看表

量词 功能
. 匹配任何字符。
+ 一次或多次修改前面的集合。
* 修改前面的集合零次或多次。
? 修改前面的集合零次或一次。
{n} 将前面的集合恰好修改 n 次。
{n,} 修改前面的集合 n 次或更多次
{n,m} 在 n 到 m 次之间修改前面的集合。

有了这些信息,我们现在可以使用它来查找所有 PHP 文件。 我们可以在命令中使用 /php$/ 来查找所有以 php.

[[email protected] public_html]$ ls -l | awk '$9 ~ /php$/ {print $1,$9}' -rw-rw-r--. config.php -rw-rw-r--. index.php -rw-rw-r--. list.php -rw-rw-r--. login.php

在当前文件夹中,只有四个 PHP 文件。 文件 php.ini 被排除,因为 php 不在字符串的末尾。

关系表达模式

默认情况下,正则表达式模式与整行匹配。 关系表达式模式将指定字段的内容与提供的模式匹配。

要将模式与字段匹配,我们需要针对模式指定比较运算符 (~):

  • 匹配行:$n ~ /pattern/
  • 不匹配行:$n !~ /pattern/

占位符 $n 是用于匹配提供的模式的字段数。 现在让我们使用我们之前的例子。

ls -l | awk '$9 ~ /php/ {print $1,$9}

$9 ~ /php/ 将第 9 个字段与单词匹配 php.

[[email protected] public_html]$ ls -l | awk '$9 ~ /php/ {print $1,$9}' -rw-rw-r--. config.php -rw-rw-r--. index.php -rw-rw-r--. list.php -rw-rw-r--. login.php -rw-rw-r--. php.ini

如果我尝试使用第一个字段(权限),则不会有任何结果,因为第一个字段只包含像 -rwxr-xr– 这样的字符。 (代表读、写、执行)。

[[email protected] public_html]$ ls -l | awk '$1 ~ /php/ {print $1,$9}' [[email protected] public_html]$

范围模式

范围模式由两个以逗号分隔的模式组成。 这允许我们打印匹配第一个模式的行中的所有记录,直到匹配第二个模式。

/pattern1/, /pattern2/

在这个例子中,我想打印从匹配 config 的行到匹配 index.txt 的文件的所有文件。 命令如下所示。

[[email protected] public_html]$ ls -l | awk '/config/,/index/ { print $0 }' -rw-rw-r--. 1 mstevens mstevens    0 Mar 19 04:48 config.php -rw-r--r--. 1 mstevens mstevens 3661 Mar 19 04:31 dovecot.log -rw-rw-r--. 1 mstevens mstevens    0 Mar 19 04:48 error_log -rwxrwxrwx. 1 mstevens mstevens    0 Mar 19 04:49 everyone.txt -rw-rw-r--. 1 mstevens mstevens    0 Mar 19 04:48 index.php

我们还可以匹配遵循定义规则的行中的字符。 假设您要查找包含该字母的所有行 ,然后是字母 或者 一世. 创建以下命令。

[[email protected] public_html]$ ls -l | awk '$9 ~ /l[oi]/ {print $1,$9}' -rw-rw-r--. access_log -rw-r--r--. dovecot.log -rw-rw-r--. error_log -rw-rw-r--. list.php -rw-rw-r--. login.php

如上图所示, 日志, 列表, 和 登录 是与 awk 命令中使用的正则表达式匹配的单词。

如果提供的文本中存在重复的特定字符,则可以使用量词。 我创建了一个包含以下内容的文件。

[[email protected] public_html]$ cat test.txt 1. a b c d 2. d c b a 3. aa bb cc dd 4. dd cc bb aa 5. aaa bbb ccc ddd 6. ddd ccc bbb aaa

查找包含三个的所有行 一种 人物 (啊) 并且至少有一个后续 C 字符,我将使用以下命令。

awk '/a{3}.*c/ {print $0}' test.txt

输出表明一行包含 啊啊啊啊 至少有一个字符 C 紧随其后。

[[email protected] public_html]$ awk '/a{3}.*c/ {print $0}' test.txt 5. aaa bbb ccc ddd

特殊表达

AWK 中的变量可以设置在程序的任何一行。 AWK 包括以下特殊模式:

  • BEGIN – 在读取第一条记录之前执行其相应的操作,通常用于为整个程序定义变量。
  • END – 在从输入文件中读取最后一条记录后执行其操作。

AWK 有几个内置变量,允许您控制程序的处理方式。 以下是一些最常见的内置变量。

查看表

多变的 功能
NF 记录中的字段数。
当前记录的编号。
文档名称 当前正在处理的输入文件的名称。
FS 字段分隔符。
RS 记录分隔符。
飞行服务队 输出字段分隔符。
口服补液盐 输出记录分隔符。

现在让我们在命令中使用 NR 来检查 test.txt 中的行数。 正如我们在下面看到的,文件中有六行。

[[email protected] public_html]# awk 'END { print FILENAME, "contains", NR, "lines." }' test.txt test.txt contains 6 lines.

更改分隔符

分隔符是将文本行分成字段的任何字符。 默认字段分隔符是任意数量的空白字符,如空格或制表符,但您可以在 awk 命令中使用 FS 变量或 -F 标志更改分隔符。

使用 FS 变量

首先,我们将展示如何使用 FS 变量。 下面我们有 test.txt 中的当前行,字段用空格分隔。

[[email protected] public_html]$ cat test.txt 1. a b c d 2. d c b a 3. aa bb cc dd 4. dd cc bb aa 5. aaa bbb ccc ddd 6. ddd ccc bbb aaa

为了更容易阅读,下图显示了上面的信息,空白区域以绿色突出显示。

现在,我将通过 C 字符并打印第一个字段。 这意味着现有的空格将不再分隔每个字段并且是常规字符。 第一次之前的一切 C 在一行中将成为第一个字段的一部分并将被打印。 行上的所有剩余信息都是后续字段的一部分,不会包含在输出中。

[[email protected] public_html]$ awk 'BEGIN { FS = "c" } { print $1 }' test.txt 1. a b 2. d 3. aa bb 4. dd 5. aaa bbb 6. ddd

同样,我们有上面显示的输出,带有分隔符 (C)。

因为分隔符创建了一个额外的字段,所以数量 C‘s on a line 将增加现有字段的数量。 第 1 行和第 2 行有两个字段,第 3 行和第 4 行有三个字段,第 5 行和第 6 行有四个字段。我们可以在下图中更好地看到这一点。 每个绿色分隔符之间的区域代表一个附加字段。

fs-variable-c-separator-all-fields

使用 -F 标志

现在我们将使用 -F 标志更改 awk 命令中的分隔符,并通过另一个示例进行工作。

awk -F'c' '{ print $1 }' test.txt

下面显示了本文前面的文件夹内容。

[[email protected] public_html]$ ls -l total 12 -rw-rw-r--. 1 mstevens mstevens 6426 Feb  9 08:00 access_log -rw-rw-r--. 1 mstevens mstevens    0 Mar 19 04:48 config.php -rw-r--r--. 1 mstevens mstevens 3661 Mar 19 04:31 dovecot.log -rw-rw-r--. 1 mstevens mstevens    0 Mar 19 04:48 error_log -rwxrwxrwx. 1 mstevens mstevens    0 Mar 19 04:49 everyone.txt -rw-rw-r--. 1 mstevens mstevens    0 Mar 19 04:48 index.php -rw-rw-r--. 1 mstevens mstevens    0 Mar 19 04:49 list.php -rw-rw-r--. 1 mstevens mstevens    0 Mar 19 04:49 login.php -rw-rw-r--. 1 mstevens mstevens    0 Mar 24 03:14 php.ini

通过使用来自 dovecot.log 的一些记录,我们可以通过合并 awk 命令来确定是否有人试图访问电子邮件帐户。 我们有失败和成功连接的例子。

查看表

连接失败 连接成功
3 月 19 日 04:21:20 主机 dovecot:imap-login:断开连接(身份验证失败,2 秒内尝试 1 次):user=,method=PLAIN,rip=50.50.50.50,lip=5.6。 7.8, TLS, TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits), session= 3 月 19 日 04:37:33 主机 dovecot:imap-login:登录:user=,method=PLAIN,rip=1.2.3.4,lip=5.6.7.8,mpid=20273,TLS,TLSv1。 2 密码 ECDHE-RSA-AES256-GCM-SHA384(256/256 位),session=

这并不漂亮,但我们可以将连接输出分成更小的部分。 这些日志中最重要的值是:

  • imap-login – 表示有人试图登录电子邮件帐户。
  • user= – 显示该人试图访问的电子邮件帐户。
  • rip= – 尝试连接的 IP。

以下命令将输出所有未能连接到电子邮件帐户的 IP。

[[email protected] public_html]$ awk -F'rip=' '/imap-login/&&/failed/ {print $1, $2}' dovecot.log | awk -F'user=" "{print $2}' | awk -F, '{print $3,$1}'   127.0.0.1 <[email protected]>   127.0.0.1 <[email protected]>   127.0.0.1 <[email protected]>   50.50.50.50 <[email protected]>   50.50.50.50 <[email protected]>   50.50.50.50 <[email protected]>   50.50.50.50 <[email protected]>   50.50.50.50 <[email protected]>   50.50.50.50 <[email protected]>   50.50.50.50 <[email protected]>   50.50.50.50 <[email protected]>   50.50.50.50 <[email protected]>

如果您看到可疑活动,则可能有人试图对您的服务器进行暴力攻击。 尽快更新您的密码,并采取措施防止将来发生攻击,例如实施双因素身份验证 (2FA) 和启用 CAPTCHA。

在 sub() 和 gsub() 中使用 AWK

AWK 具有多个执行查找和替换操作的函数,例如 sed 命令。 sub 函数用提供的字符串替换记录中的第一个匹配实体。 我将在 test.txt 文件中显示这一点。

读取 sub(/a/, “X”, $2) 的命令部分; 将替换字母 一种 用一封信 X 在第二个领域。 只有第一、第三和第五行会受到影响,因为这些行包含字母 一种 在第二场。

[[email protected] public_html]$ awk '{sub(/a/, "X", $2); print $0}' test.txt 1. X b c d 2. d c b a 3. Xa bb cc dd 4. dd cc bb aa 5. Xaa bbb ccc ddd 6. ddd ccc bbb aaa

虽然此更改只会显示在终端中并且不会更改文件,但我们可以将输出重定向到其他文件以保存更改。 当我们需要替换文件中的某些信息时使用 sub 函数,例如 sql 文件中的站点 URL,同时仍保留原始 sql 文件。

第二个函数是 gsub,虽然它具有相同的语法,但唯一的区别是它将替换在提供的字段中找到的所有值,而不仅仅是第一个字符。 同样,第一行、第三行和第五行受到影响,但不仅仅是第一行 一种 行中的字符更改为 X, 全部 一种 第一个字段中的字符更改为 X.

[[email protected] public_html]$ awk '{gsub(/a/, "X", $2); print $0}' test.txt 1. X b c d 2. d c b a 3. XX bb cc dd 4. dd cc bb aa 5. XXX bbb ccc ddd 6. ddd ccc bbb aaa

结论

AWK 是一个强大的工具,可以替代 grep、sed 和许多其他命令来查找文件中的模式。 根据需要,可以更改所有模式以输出所需的信息。 在您自己的服务器上测试本文中提到的命令,看看您能找到哪些模式!

要了解有关 Liquid Webs 解决方案的更多信息,请访问我们的产品概述页面以了解更多信息。 我们的托管托管产品线足够强大,适用于各种规模的企业,从早期初创企业到需要企业托管环境的成熟企业。