前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Awk,一行程序和脚本,帮助您对文本文件进行排序【Programming】

Awk,一行程序和脚本,帮助您对文本文件进行排序【Programming】

作者头像
Potato
修改2019-11-11 11:07:08
1.5K0
修改2019-11-11 11:07:08
举报
图片来源:Internet Archive Book Images. Modified by Opensource.com. CC BY-SA 4.0
图片来源:Internet Archive Book Images. Modified by Opensource.com. CC BY-SA 4.0

Awk是一种Unix命令,用于扫描和处理包含可预测模式的文本。然而,因为它具有函数功能,所以它也被称为编程语言。

奇怪的是,awk其实是有很多种。(或者,如果你相信只能有一种,那么就有很多种它的克隆。)其中,最初的有awk,他是由Aho,Weinberger和Kernighan编写的程序,还有nawk,mawk,和GNU版本gawk。GNU版本的awk是该实用程序的高度可移植的免费软件版本,具有几个独特的功能,因此本文是关于GNUawk的。

虽然它的正式名称是gawk,但在gnu+linux系统中,它的别名是awk,并且是该命令的默认版本。在其他没有搭载GNU awk的系统上,您必须安装它并将其称为gawk,而不是awk。本文将交替使用awk和gawk这两个术语。

作为命令和编程语言,awk成为了强大的工具,可以处理那些可能会被sort,cut,uniq和其他常见实用程序使用的任务。幸运的是,开放源代码中有很多冗余空间,因此,如果您面临是否使用awk的问题,答案应该是“也不错”。

Awk的灵活性之处在于,如果您已经确定使用awk来处理任务,那么无论前进的道路上出现什么情况,您都可以继续使用awk。这包括对数据进行排序的永恒需求,而不是将数据交付给您的顺序。

样本集

在探索 awk 的排序方法之前,先生成一个样本数据集来使用。为了你就不会被边缘案例和意想不到的复杂性分散注意力,我们需要它保持简单。这是本文使用的示例集:

代码语言:javascript
复制
Aptenodytes;forsteri;Miller,JF;1778;Emperor
Pygoscelis;papua;Wagler;1832;Gentoo
Eudyptula;minor;Bonaparte;1867;Little Blue
Spheniscus;demersus;Brisson;1760;African
Megadyptes;antipodes;Milne-Edwards;1880;Yellow-eyed
Eudyptes;chrysocome;Viellot;1816;Sothern Rockhopper
Torvaldis;linux;Ewing,L;1996;Tux

这是一个很小的数据集,但是它提供了各种各样的数据类型:

  • 属名和种名,彼此相关但被认为是分开的
  • 姓,有时以逗号开头的首字母缩写
  • 代表日期的整数
  • 任意术语
  • 所有字段均以分号分隔

根据您的知识您可以将其视为一个2D 数组或表,或者仅仅是一个以行分隔的数据集合。 如何看待它取决于你自己,因为awk只会处理文本,需要由您指定如何解析它。

sort命令

如果您只想按特定的,可定义的字段(例如电子表格中的“单元格”)对文本数据集进行排序,则可以使用sort命令

字段和记录

无论输入数据的格式如何,您都必须在其中找到一种模式,以便能够专注于最重要的数据部分。在本例中,数据由两种分隔: 行和字段。 每一行代表一条新记录,就像您在电子表格或数据库转储中可能看到的那样。 在每一行中,都有不同的字段(可以将它们看作电子表格中的单元格) ,这些字段之间用分号分隔(;)。

Awk 一次处理一条记录,因此当您构建将要给 Awk 的指令时,您可以只关注一行。 用一行建立你想要做的事情,然后在下一行或者更多行测试它(无论是心理上还是用awk进行测试)。 最后,您将得到一个很好的假设,即 awk 脚本必须执行哪些操作才能为您提供所需的数据结构。

在这种情况下,很容易看出每个字段都由分号分隔。 为了简单起见,假设您希望根据每行的第一个字段对列表进行排序。

在进行排序之前,必须能够将 awk 集中在每行的第一个字段上,因此这是第一步。 终端中awk命令的语法为awk ,后跟相关选项,然后是awk命令,最后是要处理的数据文件。

代码语言:javascript
复制
$ awk --field-separator=";" '{print $1;}' penguins.list
Aptenodytes
Pygoscelis
Eudyptula
Spheniscus
Megadyptes
Eudyptes
Torvaldis

因为字段分隔符是对 Bash shell 具有特殊意义的字符,所以必须将分号包含在引号中,或者在前面加上反斜杠。 这个命令仅用于证明您可以关注特定的字段。 你可以使用另一个字段的编号来查看你的数据的另一个“列”的内容:

代码语言:javascript
复制
$ awk --field-separator=";" '{print $3;}' penguins.list
Miller,JF
Wagler
Bonaparte
Brisson
Milne-Edwards
Viellot
Ewing,L

目前还没有任何排序结果,但这是很好的基础工作。

脚本

Awk 不仅仅是一个命令; 它是一种编程语言,具有索引、数组和函数。 这一点很重要,因为它意味着您可以获取一个要进行排序的字段列表,将该列表存储在内存中,对其进行处理,然后输出结果数据。对于诸如此类的一系列复杂操作,在文本文件中进行操作会更容易,因此请创建一个名为sorter.awk的新文件并输入以下文本:

代码语言:javascript
复制
#!/usr/bin/awk -f 
BEGIN {
 FS=";";
}

这会将文件建立为awk脚本,该脚本执行文件中包含的行。

BEGIN语句是awk提供的特殊设置功能,用于只需要执行一次的任务。 定义内置变量FS ,它表示字段分隔符,并且与在--field-separator中的awk命令中设置的值相同,只需执行一次,因此它包含在BEGIN语句中。

在 awk 中的数组

您已经知道如何通过使用 $符号和字段号收集特定字段的值,但是在这种情况下,您需要将其存储在数组中,而不是将其打印到终端。 这是通过 awk 数组完成的。 Awk 数组的重要之处在于它包含键和值。 想象一下这篇文章的数组; 它看起来像这样: author: “seth” ,title:”How to sort with awk”,length: 1200。 像 author、 title 和 length 这样的元素是键,下面的内容是值。

在排序的上下文中,这样做可以将任何字段分配为键,将任何记录分配为值,然后使用内置的awk函数asorti()(按索引排序)按键值进行排序。现在,假设您只希望按第二个字段进行排序。

前面没有特殊关键字BEGIN或END的Awk语句是发生在每个记录上的循环。这是脚本的一部分,它扫描数据中的模式并相应地处理它。每次awk将注意力转向一个记录时,都会执行{}中的语句,除非前面是BEGIN或END。

为了向数组添加键和值,创建一个包含数组的变量(在这个示例脚本中,我称之为 ARRAY,它并不是非常原始,但非常利于理解),然后在方括号中将其分配给键和一个等号。

代码语言:javascript
复制
{ # dump each field into an array
 ARRAY[$2] = $R;
}

在这个语句中,第二个字段($2)的内容用作关键词,当前记录($r)用作值。

asorti()函数

除了数列之外,awk 还有几个基本函数,您可以将它们用作常见任务的快速简单解决方案。 Gnu awk 中引入的函数之一 asorti ()提供了按键(索引)或值对数组进行排序的能力。

您只能在对数组进行填充后对其进行排序,这意味着该操作不能在每个新记录中发生,而只能在脚本的最后阶段发生。 为此,awk 提供了特殊的 END 关键字。 与 BEGIN 相反,END 语句只在所有记录被扫描之后发生一次。

把这个添加到你的脚本中:

代码语言:javascript
复制
END {
  asorti(ARRAY,SARRAY);
 # get length
 j = length(SARRAY);
 for (i = 1; i <= j; i++) {
 printf("%s %s\n", SARRAY[i],ARRAY[SARRAY[i]])
 }
}

asorti()函数获取ARRAY的内容,根据索引对其进行排序,并将结果放入一个名为SARRAY的新数组中(这是我为本文发明的名称,意为排序数组)。

接下来,为变量j分配length()函数的结果,该函数计算SARRAY中的项数。

最后,使用for循环迭代SARRAY中的每个项,使用printf()函数打印每个键,然后在ARRAY中打印该键的相应值。

运行脚本

运行awk脚本,使其可执行:

代码语言:javascript
复制
$ chmod +x sorter.awk

然后针对penguin.list示例数据运行它:

代码语言:javascript
复制
$ ./sorter.awk penguins.list 
antipodes Megadyptes;antipodes;Milne-Edwards;1880;Yellow-eyed
chrysocome Eudyptes;chrysocome;Viellot;1816;Sothern Rockhopper
demersus Spheniscus;demersus;Brisson;1760;African
forsteri Aptenodytes;forsteri;Miller,JF;1778;Emperor
linux Torvaldis;linux;Ewing,L;1996;Tux
minor Eudyptula;minor;Bonaparte;1867;Little Blue
papua Pygoscelis;papua;Wagler;1832;Gentoo

如您所见,数据将按第二个字段进行排序。

但这有点限制性。 最好能够在运行时灵活地选择要使用哪个字段作为排序键,这样就可以在任何数据集上使用此脚本,并获得有意义的结果。

添加命令选项

您可以通过在脚本中使用var将命令变量添加到awk脚本中。 更改脚本,以便在创建数组时迭代子句使用var :

代码语言:javascript
复制
{ # dump each field into an array
 ARRAY[$var] = $R;
}

尝试运行脚本,使用-v var选项对第三个字段进行排序:

代码语言:javascript
复制
$ ./sorter.awk -v var=3 penguins.list 
Bonaparte Eudyptula;minor;Bonaparte;1867;Little Blue
Brisson Spheniscus;demersus;Brisson;1760;African
Ewing,L Torvaldis;linux;Ewing,L;1996;Tux
Miller,JF Aptenodytes;forsteri;Miller,JF;1778;Emperor
Milne-Edwards Megadyptes;antipodes;Milne-Edwards;1880;Yellow-eyed
Viellot Eudyptes;chrysocome;Viellot;1816;Sothern Rockhopper
Wagler Pygoscelis;papua;Wagler;1832;Gentoo

总结

本文演示了如何在纯GNU awk中对数据进行排序。 该脚本可以进行改进,因此如果它对您有用,请花一些时间在gawk的手册上研究awk函数并自定义脚本以获得更好的输出。

以下是到目前为止的完整脚本:

代码语言:javascript
复制
#!/usr/bin/awk -f
# GPLv3 appears here
# usage: ./sorter.awk -v var=NUM FILE
BEGIN { FS=";"; }
{ # dump each field into an array
 ARRAY[$var] = $R;
}
END {
 asorti(ARRAY,SARRAY);
 # get length
 j = length(SARRAY);
 for (i = 1; i <= j; i++) {
 printf("%s %s\n", SARRAY[i],ARRAY[SARRAY[i]])
 }
}

本文系外文翻译,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系外文翻译前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 样本集
  • sort命令
  • 字段和记录
  • 脚本
  • 在 awk 中的数组
  • asorti()函数
  • 运行脚本
  • 添加命令选项
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档