了解如何使用 AWK 命令

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

使用 AWK,我们可以:

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

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

[mstevens@host 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. 文件名

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

[mstevens@host 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 支持不同类型的模式:

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

正则表达式模式

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

[mstevens@host 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 中也广泛使用的“正则表达式”。

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

锚点

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

查看表

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

人物

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

查看表

特点功能
[ae]选择 一种 或者 e.
[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.

[mstevens@host 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 是用于匹配提供的模式的字段数。 现在让我们使用我们之前的 example.

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

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

[mstevens@host 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– 之类的字符。 (代表读、写、执行)。

[mstevens@host public_html]$ ls -l | awk '$1 ~ /php/ {print $1,$9}'
[mstevens@host public_html]$

范围模式

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

/pattern1/, /pattern2/

在这个 example 我想打印从匹配配置的行到匹配索引的文件的所有文件。 该命令如下所示。

[mstevens@host 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

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

[mstevens@host 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 命令中使用的正则表达式匹配的单词。

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

[mstevens@host 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 之后。

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

特殊表达

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

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

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

查看表

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

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

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

更改分隔符

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

使用 FS 变量

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

[mstevens@host 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 in a line 将成为第一个字段的一部分并将被打印。 行上的所有剩余信息都是后续字段的一部分,不会包含在输出中。

[mstevens@host 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-变量-c-分隔符-所有字段

使用 -F 标志

现在我们将使用 -F 标志更改 awk 命令中的分隔符并处理另一个 example.

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

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

[mstevens@host 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,带密码 ECDHE-RSA-AES256-GCM-SHA384(256/256 位),会话=Mar 19 04:37:33 host dovecot: imap-login: 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 位),会话=

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

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

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

[mstevens@host 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。

将 AWK 与 sub() 和 gsub() 一起使用

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

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

[mstevens@host 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.

[mstevens@host 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 解决方案的更多信息,请访问我们的产品概览页面以了解更多信息。 我们的托管托管产品系列对于各种规模的企业来说都足够强大,从早期初创企业到需要企业托管环境的成熟企业。