介绍
Linux 实用程序通常遵循 Unix 的设计理念。 鼓励工具体积小,使用纯文本文件进行输入和输出,并以模块化方式运行。 由于这一传统,我们使用 sed 和 awk
等工具拥有出色的文本处理功能。
awk
是一种编程语言和文本处理器,您可以使用它以非常有用的方式操作文本数据。 在本指南中,您将探索如何使用 awk
命令行工具以及如何使用它来处理文本。
启动交互式终端!
基本语法
awk
命令默认包含在所有现代 Linux 系统中,因此您无需安装即可开始使用它。
awk
在处理以可预测方式格式化的文本文件时最有用。 例如,它在解析和处理表格数据方面表现出色。 它逐行运行并遍历整个文件。
默认情况下,它使用空格(空格、制表符等)来分隔字段。 幸运的是,Linux 系统上的许多配置文件都使用这种格式。
awk
命令的基本格式是:
awk '/search_pattern/ { action_to_take_on_matches; another_action; }' file_to_parse
您可以从任何 awk
命令中省略搜索部分或操作部分。 默认情况下,如果未给出“action”部分,则执行的操作是“print”。 这只是打印所有匹配的行。
如果没有给出搜索部分,awk
将执行每行列出的操作。
如果两者都给出,awk
使用搜索部分来确定当前行是否反映了模式,然后对匹配项执行操作。
在最简单的形式中,您可以使用 awk
像 cat
将文本文件的所有行打印到屏幕上。
创建一个 favorite_food.txt
文件,其中列出了一群朋友最喜欢的食物:
echo "carrot sandy wasabi luke sandwich brian salad ryan spaghetti jessica" > favorite_food.txt
现在使用 awk
命令将文件打印到屏幕上:
awk '{print}' favorite_food.txt
您将看到文件打印到屏幕上:
Outputcarrot sandy wasabi luke sandwich brian salad ryan spaghetti jessica
这不是很有用。 让我们通过在文件中搜索文本“sand”来试用 awk
的搜索过滤功能:
awk '/sand/' favorite_food.txt
Outputcarrot sandy sandwich brian
如您所见,awk
现在只打印包含字符“sand”的行。
使用正则表达式,您可以定位文本的特定部分。 要仅显示以字母“sand”开头的行,请使用正则表达式 ^sand
:
awk '/^sand/' favorite_food.txt
这次只显示一行:
Outputsandwich brian
同样,您可以使用操作部分来指定要打印的信息。 例如,要仅打印第一列,请使用以下命令:
awk '/^sand/ {print $1;}' favorite_food.txt
Outputsandwich
您可以通过与其列号关联的变量引用每一列(由空格分隔)。 例如,第一列是$1
,第二列是$2
,可以用$0
引用整行。
内部变量和扩展格式
awk
命令在处理文件时使用一些内部变量来分配某些信息。
awk
使用的内部变量是:
- FILENAME:引用当前输入文件。
- FNR:引用当前记录相对于当前输入文件的编号。 例如,如果您有两个输入文件,这将告诉您每个文件的记录号,而不是总数。
- FS:当前字段分隔符,用于表示记录中的每个字段。 默认情况下,这设置为空白。
- NF:当前记录的字段数。
- NR:当前记录的编号。
- OFS:输出数据的字段分隔符。 默认情况下,这设置为空白。
- ORS:输出数据的记录分隔符。 默认情况下,这是一个换行符。
- RS:记录分隔符,用于区分输入文件中的单独记录。 默认情况下,这是一个换行符。
您可以随意更改这些变量的值以匹配文件的需要。 通常您在处理的初始化阶段执行此操作。
这给我们带来了另一个重要的概念。 awk
语法比您目前使用的稍微复杂一些还有可选的 BEGIN
和 END
块可以包含在文件处理之前和之后执行的命令, 分别。
这使得我们的扩展语法看起来像这样:
awk 'BEGIN { action; } /search/ { action; } END { action; }' input_file
BEGIN
和 END
关键字是特定的条件集,就像搜索参数一样。 它们在文档处理之前和之后匹配。
这意味着您可以更改 BEGIN
部分中的一些内部变量。 例如,/etc/passwd
文件用冒号 (:
) 而非空格分隔。
要打印出此文件的第一列,请执行以下命令:
awk 'BEGIN { FS=":"; } { print $1; }' /etc/passwd
Outputroot daemon bin sys sync games man . . .
您可以使用 BEGIN
和 END
块来打印有关正在打印的字段的信息。 使用以下命令将文件中的数据转换为表格,使用 \t
与制表符很好地隔开:
awk 'BEGIN { FS=":"; print "User\t\tUID\t\tGID\t\tHome\t\tShell\n--------------"; } {print $1,"\t\t",$3,"\t\t",$4,"\t\t",$6,"\t\t",$7;} END { print "---------\nFile Complete" }' /etc/passwd
你会看到这个输出:
OutputUser UID GID Home Shell -------------- root 0 0 /root /bin/bash daemon 1 1 /usr/sbin /bin/sh bin 2 2 /bin /bin/sh sys 3 3 /dev /bin/sh sync 4 65534 /bin /bin/sync . . . --------- File Complete
如您所见,您可以利用 awk
的一些特性很好地格式化内容。
每个扩展部分都是可选的。 事实上,如果定义了另一个部分,则主要操作部分本身是可选的。 例如,您可以执行以下操作:
awk 'BEGIN { print "We can use awk like the echo command"; }'
你会看到这个输出:
OutputWe can use awk like the echo command
现在让我们看看如何在输出的字段中查找文本。
字段搜索和复合表达式
在前面的一个示例中,您打印了 favorite_food.txt
文件中以“sand”开头的行。 这很容易,因为您正在寻找整行的开头。
如果您想查明搜索模式是否在 字段 的开头匹配,该怎么办?
创建一个新版本的 favorite_food.txt
文件,在每个人的食物前添加一个项目编号:
echo "1 carrot sandy 2 wasabi luke 3 sandwich brian 4 salad ryan 5 spaghetti jessica" > favorite_food.txt
如果你想从这个文件中找到所有以“sa”开头的食物,你可以先尝试这样的事情:
awk '/sa/' favorite_food.txt
这显示了所有包含“sa”的行:
Output1 carrot sandy 2 wasabi luke 3 sandwich brian 4 salad ryan
在这里,您匹配单词中的任何“sa”实例。 这最终包括诸如中间有图案的“芥末”或不在您想要的列中的“桑迪”之类的东西。 在这种情况下,您只对 second 列中带有“sa”的单词 beginning 感兴趣。
您可以使用以下命令告诉 awk
仅匹配第二列的开头:
awk '$2 ~ /^sa/' favorite_food.txt
如您所见,这允许我们仅在第二列的开头搜索匹配项。
field_num ~
部分指定 awk
应该只关注第二列。
Output3 sandwich brian 4 salad ryan
您可以通过包含“!”轻松搜索 不 匹配的内容。 波浪号 (~) 之前的字符。 此命令将返回所有 not 有以“sa”开头的食物的行:
awk '$2 !~ /^sa/' favorite_food.txt
Output1 carrot sandy 2 wasabi luke 5 spaghetti jessica
如果您稍后决定只对不以“sa”开头且项目编号小于 5 的行感兴趣,则可以使用如下复合表达式:
awk '$2 !~ /^sa/ && $1 < 5' favorite_food.txt
这引入了一些新概念。 第一个是使用 &&
运算符为要匹配的行添加附加要求的能力。 使用它,您可以组合任意数量的条件来匹配行。 在本例中,您使用此运算符添加检查第一列的值是否小于 5。
你会看到这个输出:
Output1 carrot sandy 2 wasabi luke
您可以使用 awk
处理文件,但也可以处理其他程序的输出。
处理其他程序的输出
您可以使用 awk
命令解析其他程序的输出,而不是指定文件名。 例如,您可以使用 awk
从 ip
命令中解析出 IPv4 地址。
ip a
命令显示您机器上所有网络接口的 IP 地址、广播地址和其他信息。 要显示名为 eth0
的接口的信息,请使用以下命令:
ip a s eth0
您将看到以下结果:
Output2571: eth0@if2572: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:0b brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.11/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever
您可以使用 awk
定位 inet
行,然后只打印出 IP 地址:
ip a s eth0 | awk -F '[\/ ]+' '/inet / {print $3}'
-F
标志告诉 awk
使用正则表达式 [\/ ]+
用正斜杠或空格分隔。 这会将 inet 172.17.0.11/16
行拆分为单独的字段。 IP 地址位于第三个字段中,因为行首的空格也算作一个字段,因为您用空格和斜杠分隔。 请注意,在这种情况下,awk
将连续空格视为单个空格。
输出显示 IP 地址:
Output172.17.0.11
您会发现很多地方可以使用 awk
来搜索或解析其他命令的输出。
结论
到目前为止,您应该对如何使用 awk
命令来操作、格式化和有选择地打印文本文件和文本流有了基本的了解。 不过,Awk 是一个更大的话题,实际上是一门完整的编程语言,包括变量赋值、控制结构、内置函数等等。 您可以在自己的脚本中使用它以可靠的方式格式化文本。
要了解有关 awk
的更多信息,您可以阅读其创建者 编写的 免费公共领域书籍,其中更详细。