Awk是一个强大的工具,可以执行某些任务,这些任务可能由其他常用实用程序完成,包括排序。Awk是一个普遍存在的Unix命令,用于扫描和处理包含可预测模式的文本。但是,由于它的功能,它也可以合法地称为编程语言。令人困惑的是,有不止一个awk。(或者,如果您认为只有一个,其他都是克隆。)有awk(Aho、Weinberger和Kernighan编写的原始程序),然后是nawk、mawk和gawk的GNU版本。awk的GNU版本是该实用程序的高度可移植的免费软件版本,具有几个独特的功能,因此本文是关于GNUawk的。虽然它的正式名称是gawk,但在GNU+Linux系统上它被别名为awk并用作该命令的默认版本。在其他没有附带GNUawk的系统上,您必须先安装它并称它为gawk,而不是awk。本文交替使用术语awk和gawk。awk既是一种命令语言又是一种编程语言,这使它成为一个强大的工具,可以处理那些原本要交给sort、cut、uniq和其他常用实用程序执行的任务。幸运的是,开源中有很大的冗余空间,所以如果您面临是否使用awk的问题,答案可能是肯定的,“随便”。awk灵活性的美妙之处在于,如果您决定使用awk完成一项任务,那么无论接下来发生什么,您都可以继续使用awk。这包括永远需要订购数据,而不是按照它交付给您的顺序。示例数据集在探索awk的排序方法之前,生成一个示例数据集以供使用。保持简单,这样您就不会陷入极端情况和意想不到的复杂性之中。这是本文中使用的样本集:Aptenodytes;福斯特里;米勒,JF;1778;Pygoscelis皇帝;巴布亚;瓦格勒;1832年;GentooEudyptula;次要的;波拿巴;1867年;;1880;Yellow-eyedEudyptes;chrysocome;Viellot;1816;SothernRockhopperTorvaldis;linux;Ewing,L;1996;Tux这是一个小数据集,但它提供了多种数据类型:属名和种名,相互关联但是再次分隔姓氏,有时首字母以逗号开头代表日期的整数任何术语所有字段都用分号分隔根据您的教育,您可能认为这是一个二维数组或表格或只是行分隔的数据集。你如何看待它只是你的问题,而awk只理解文本。由你告诉awk你想如何解析它。只想排序如果只想按特定的可定义字段(例如电子表格中的“单元格”)对文本数据集进行排序,则可以使用排序命令。字段和记录无论以何种格式输入,都必须在其中找到模式,以便专注于对您重要的数据部分。在此示例中,数据由两个因素分隔:行和字段。每行代表一条新记录,就像您在电子表格或数据库转储中看到的一样。在每一行中,都有不同的字段,用分号(;)分隔(将它们视为电子表格中的单元格)。awk一次只处理一条记录,因此当您构造awk的命令时,您只能关注一行记录。在一行数据上写下你想做的事情,然后在下一行测试它(在脑子里或用awk),然后再进行一些其他测试。最后,您必须对awk脚本将要处理的数据做出假设,以便它可以为您提供所需数据结构中的数据。在此示例中,很容易看出每个字段都用分号分隔。为简单起见,假设您要按每行的第一个字段对列表进行排序。您必须能够让awk在排序之前只关注每行的第一个字段,所以这是第一步。终端中awk命令的语法是awk,后面是相关的选项,最后是要处理的数据文件。$awk--field-separator=";"'{print$1;}'penguins.listAptenodytesPygoscelisEudyptulaSpheniscusMegadyptesEudyptesTorvaldis因为字段分隔符是对Bashshell具有特殊含义的字符,所以分号必须用引号括起来或前面有反斜杠。此命令仅用于演示您可以专注于特定领域。你可以用另一个字段的编号尝试相同的命令来查看另一个数据“列”的内容:$awk--field-separator=";"'{print$3;}'penguins.listMiller,JFWaglerBonaparteBrissonMilne-EdwardsViellotEwing,L我们还没有进行任何排序,但这是一个很好的基础。脚本awk不仅仅是命令,它是一种具有索引、数组和函数的编程语言。这很重要,因为这意味着您可以获取要排序的字段列表、将列表存储在内存中、处理它并打印结果数据。对于像这样的一系列复杂操作,在文本文件中更容易完成,因此创建一个名为sort.awk的新文件并输入以下文本:#!/bin/gawk-fBEGIN{FS=";";这会将文件创建为执行行的awk脚本。BEGIN语句是awk为只需执行一次的任务提供的特殊设置功能。定义了内置变量FS,代表字段分隔符,与awk命令中--field-separator设置的值相同,只需要执行一次,所以包含在BEGIN语句中。awk中的数组您已经知道如何使用$符号和字段编号来收集特定字段的值,但在这种情况下,您需要将其存储在数组中而不是将其打印到终端。这是通过awk数组完成的。awk数组的重要之处在于它包含键和值。想象一下这篇文章的内容;它看起来像这样:作者:“seth”,标题:“如何使用awk进行排序”,长度:1200。作者、标题和长度等元素是键,后面是值。在排序上下文中这样做的好处是,您可以将任何字段指定为键,将任何记录指定为值,然后使用内置的awk函数asorti()(按索引排序)按键排序。现在,假设您只想按第二个字段排序。未包含在特殊关键字BEGIN或END中的awk语句是针对每条记录执行的循环。这是脚本的一部分,用于扫描数据以查找相应的模式和过程。{}内的语句在每次awk关注记录时执行(除非前面有BEGIN或END)。要向数组添加键和值,请创建一个包含该数组的变量(在这个示例脚本中,我将其称为ARRAY,不完全是字面意思,但很清楚),并在方括号中为其分配键和值用等号(=)连接。{#dump每个字段到一个数组中ARRAY[$2]=$R;}在这个语句中,第二个字段的内容($2)作为键,当前记录($R)作为值。asorti()函数除了数组,awk还有一些基本函数,您可以将它们用作常见任务的快速简便解决方案。GNUawk中引入的函数之一asorti()提供了按键(索引)或值对数组进行排序的能力。您只能在填充数组后对其进行排序,这意味着不能为每条新记录触发此操作,而只能在脚本的最后。为此,awk提供了特殊的END关键字。与BEGIN不同,END语句仅在扫描完所有记录后触发一次。将这些添加到您的脚本中:END{asorti(ARRAY,SARRAY);#得到长度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脚本,首先使其可执行:$chmod+xsorter.awk然后针对penguin.list示例数据运行它:$./sorter.awkpenguins.listantipodesMegadyptes;antipodes;Milne-Edwards;1880;黄眼金鱼Eudyptes;chrysocome;Viellot;1816;SothernRockhopperdemersusSpheniscus;demersus;Brisson;1760;AfricanforsteriAptenodytes;forsteri;Miller,JF;1778;Bonaparte;1867;LittleBluepapuaPygoscelis;papua;Wagler;1832;Gentoo如您所见,数据按第二个字段排序。这有点限制。如果能够在运行时灵活地选择要用作排序键的字段,这样该脚本就可以用于任何数据集并获得有意义的结果,那就太好了。添加命令选项您可以通过在脚本中使用文本值var将命令变量添加到awk脚本。更改脚本,以便迭代子句在创建数组时使用var:{#dumpeachfieldintoanarrayARRAY[$var]=$R;}尝试运行脚本,以便在执行时使用-vvar选项转储脚本按第三个字段排序:$./sorter.awk-vvar=3penguins.listBonaparteEudyptula;minor;Bonaparte;1867;LittleBlueBrissonSpheniscus;demersus;Brisson;1760;AfricanEwing,LTorvaldis;linux;Ewing,L;1996;TuxMiller,JFAptenodytes;forsteri;Miller,JF;1778;EmperorMilne-EdwardsMegadyptes;本文演示了如何在纯GNUawk中对数据进行排序。您可以通过花一些时间研究gawk手册页上的awk函数并自定义脚本以获得更好的输出来改进脚本以使其适合您。这是到目前为止的完整脚本:#!/usr/bin/awk-f#GPLv3出现在这里#usage:./sorter.awk-vvar=NUMFILEBEGIN{FS=";";}{#将每个字段转储到数组中ARRAY[$var]=$R;}END{asorti(ARRAY,SARRAY);#得到长度j=length(SARRAY);for(i=1;i<=j;i++){printf("%s%s\n",SARRAY[i],ARRAY[SARRAY[i]])}}
