帮助你排序文本文件的 Awk 命令行或脚本(推荐)

Awk 是一个强大的工具,可以执行某些可能由其它常见实用程序(包括 sort)来完成的任务。

Awk 是个普遍存在的 Unix 命令,用于扫描和处理包含可预测模式的文本。但是,由于它具有函数功能,因此也可以合理地称之为编程语言。

令人困惑的是,有不止一个 awk。(或者,如果你认为只有一个,那么其它几个就是克隆。)有 awk(由Aho、Weinberger 和 Kernighan 编写的原始程序),然后有 nawk 、mawk 和 GNU 版本的 gawk。GNU 版本的 awk 是该实用程序的一个高度可移植的自由软件版本,具有几个独特的功能,因此本文是关于 GNU awk 的。

虽然它的正式名称是 gawk,但在 GNU+Linux 系统上,它的别名是 awk,并用作该命令的默认版本。 在其他没有带有 GNU awk 的系统上,你必须先安装它并将其称为 gawk,而不是 awk。本文互换使用术语 awk 和 gawk。

awk 既是命令语言又是编程语言,这使其成为一个强大的工具,可以处理原本留给 sort、cut、uniq 和其他常见实用程序的任务。幸运的是,开源中有很多冗余空间,因此,如果你面临是否使用 awk 的问题,答案可能是肯定的“随便”。

awk 的灵活之美在于,如果你已经确定使用 awk 来完成一项任务,那么无论接下来发生什么,你都可以继续使用 awk。这包括对数据排序而不是按交付给你的顺序的永恒需求。

样本数据集

在探索 awk 的排序方法之前,请生成要使用的样本数据集。保持简单,这样你就不会为极端情况和意想不到的复杂性所困扰。这是本文使用的样本集:

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

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

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

根据你的教育背景,你可能会认为这是二维数组或表格,或者只是行分隔的数据集合。你如何看待它只是你的问题,而 awk 只认识文本。由你决定告诉 awk 你想如何解析它。

只想排序

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

字段和记录

无论输入的格式如何,都必须在其中找到模式才可以专注于对你重要的数据部分。在此示例中,数据由两个因素定界:行和字段。每行都代表一个新的记录,就如你在电子表格或数据库转储中看到的一样。在每一行中,都有用分号(;)分隔的不同的字段(将其视为电子表格中的单元格)。

awk 一次只处理一条记录,因此,当你在构造发给 awk 的这指令时,你可以只关注一行记录。写下你想对一行数据执行的操作,然后在下一行进行测试(无论是心理上还是用 awk 进行测试),然后再进行其它的一些测试。最后,你要对你的 awk 脚本要处理的数据做好假设,以便可以按你要的数据结构提供给你数据。

在这个例子中,很容易看到每个字段都用分号隔开。为简单起见,假设你要按每行的第一字段对列表进行排序。

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

$ awk --field-separator=";" '{print $1;}' penguins.list
Aptenodytes
Pygoscelis
Eudyptula
Spheniscus
Megadyptes
Eudyptes
Torvaldis

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

$ awk --field-separator=";" '{print $3;}' penguins.list
Miller,JF
Wagler
Bonaparte
Brisson
Milne-Edwards
Viellot
Ewing,L

我们尚未进行任何排序,但这是良好的基础。

脚本编程

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

#!/bin/gawk -f
BEGIN {
    FS=";";
}

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

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

awk 中的数组

你已经知道如何通过使用 $ 符号和字段编号来收集特定字段的值,但是在这种情况下,你需要将其存储在数组中而不是将其打印到终端。这是通过 awk 数组完成的。awk 数组的重要之处在于它包含键和值。 想象一下有关本文的内容;它看起来像这样:author:"seth",title:"How to sort with awk",length:1200。诸如作者、标题和长度之类的元素是键,跟着的内容为值。

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

没有被特殊关键字 BEGIN 或 END 引起来的 awk 语句是在每个记录都要执行的循环。这是脚本的一部分,该脚本扫描数据中的模式并进行相应的处理。每次 awk 将注意力转移到一条记录上时,都会执行 {} 中的语句(除非以 BEGIN 或 END 开头)。

要将键和值添加到数组,请创建一个包含数组的变量(在本示例脚本中,我将其称为 ARRAY,虽然不是很原汁原味,但很清楚),然后在方括号中分配给它键,用等号(=)连接值。

{  # dump each field into an array
  ARRAY[$2] = $R;
}

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

asorti() 函数

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

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

将这些添加到你的脚本:

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 的新数组(我在本文中发明的任意名称,表示“排序的 ARRAY”)。

接下来,将变量 j(另一个任意名称)分配给 length() 函数的结果,该函数计算 SARRAY 中的项数。

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

运行该脚本

要运行你的 awk 脚本,先使其可执行:

$ chmod +x sorter.awk

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

$ ./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:

{ # dump each field into an array
  ARRAY[$var] = $R;
}

尝试运行该脚本,以便在执行脚本时使用 -v var 选项将其按第三字段排序:

$ ./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 函数并自定义脚本以获得更好的输出。

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

#!/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]])
  }
}

总结

以上所述是小编给大家介绍的帮助你排序文本文件的 Awk 命令行或脚本,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

(0)

相关推荐

  • Shell脚本中awk指令的用法

    语法格式:awk [选项]  '指令'  操作文件 常用选项:-F  指定分隔符,分隔符用""引起来 -v:var=value在awk程序开始之前指定一个值valu给变量var,这些变量值用于awk程序的BEGIN快 -f:后面跟一个保存了awk程序的文件,代替在命令行指定awk程序 实例1:在命令行直接输入awk指令 awk '{print}' 1.txt #逐行读取文件1.txt内容并打印 awk '{print $0}' 1.txt #逐行读取文件内容,并打印该行,$0保存的是当

  • linux awk时间计算脚本及awk命令详解

    在linux如果计划时间是个麻烦事, 用awk脚本如下 BEGIN {FS=":";OFS=":"} {total_seconds=total_seconds+$3} total_seconds>=60 {total_seconds=total_sconds-60 $2=$2+1 } {total_minutes=total_minutes+$2 $2=$2+1 } {total_minutes=total_minutes+$2} total_minutes&

  • linux Shell脚本里面把一个数组传递到awk内部进行处理

    前段时间和几位同事讨论过一个问题:Shell脚本里面怎样把一个数组传递到awk内部进行处理? 当时没有找到方法.前两天在QQ群里讨论awk的时候,无意间又聊起这个话题.机缘巧合之下找到一个思路,特此分享. 测试环境: [root]# head -1 /etc/redhat-release Red Hat Enterprise Linux Server release 6.5 (Santiago) [root]# awk --version | head -1 GNU Awk 3.1.7 众所周知

  • 如何将 awk 脚本移植到 Python

    将一个 awk 脚本移植到 Python 主要在于代码风格而不是转译. 脚本是解决问题的有效方法,而 awk 是编写脚本的出色语言.它特别擅长于简单的文本处理,它可以带你完成配置文件的某些复杂重写或目录中文件名的重新格式化. 何时从 awk 转向 Python 但是在某些方面,awk 的限制开始显现出来.它没有将文件分解为模块的真正概念,它缺乏质量错误报告,并且缺少了现在被认为是编程语言工作原理的其他内容.当编程语言的这些丰富功能有助于维护关键脚本时,移植将是一个不错的选择. 我最喜欢的完美移植

  • shell脚本之正则表达式、grep、sed、awk

    --正则-- 基础正则 ^word     ##搜索以word开头的 vi/vim中 ^ 一行的开头 word$     ##搜索以word结尾的 vi/vim中 $ 一行的结尾 ^$        ##表示空行 .         ##代表且只能代表任意一个字符 \         ##例:\. 只代表点本身,转义符号,让有特殊身份意义的字符,脱掉马甲,还原 \n        ##换行符 \r        ##匹配回车 \w         ##匹配任意一个字符和数字 *        

  • 帮助你排序文本文件的 Awk 命令行或脚本(推荐)

    Awk 是一个强大的工具,可以执行某些可能由其它常见实用程序(包括 sort)来完成的任务. Awk 是个普遍存在的 Unix 命令,用于扫描和处理包含可预测模式的文本.但是,由于它具有函数功能,因此也可以合理地称之为编程语言. 令人困惑的是,有不止一个 awk.(或者,如果你认为只有一个,那么其它几个就是克隆.)有 awk(由Aho.Weinberger 和 Kernighan 编写的原始程序),然后有 nawk .mawk 和 GNU 版本的 gawk.GNU 版本的 awk 是该实用程序的

  • 解决Python 命令行执行脚本时,提示导入的包找不到的问题

    在Pydev能正常执行的脚本,在导出后在命令行执行,通常会报自己写的包导入时找不到. 一:报错原因 在PyDev中,test.py 中导入TestUserCase里面的py文件时,会写: from TestUserCase.Test import Test 这在命Pydev中没错,但是在命令行中确出现错误. 因为在PyDev中默认路径除了当前文件所在路劲外,还有工程根路径.而在命令行中只有当前文件所在路径.TestUserCase是test.py文件的上一级目录,所以在命令行中test.py i

  • python环境路径配置以及命令行运行脚本

    本文实例为大家分享了python环境路径设置方法,以及命令行运行python脚本,供大家参考,具体内容如下 找Python安装目录,设置环境路径以及在命令行运行python脚本 第一点:找Python安装目录 方法一: 方法二: 输入import sys print(sys.path) 化黑线处 第二点:找到安装目录后就可以开始设置环境变量 这里我的安装目录为C:\Program Files\Python36 再字符串的末尾,加一个分号; 然后再输入你安装python的路径,如图所示 一路点确定

  • 用Python实现命令行闹钟脚本实例

    前言: 这篇文章给大家介绍了怎样用python创建一个简单的报警,它可以运行在命令行终端,它需要分钟做为命令行参数,在这个分钟后会打印"wake-up"消息,并响铃报警,你可以用0分钟来测试,它会立即执行,用扬声器控制面板调整声音. 以下是脚本: # alarm_clock.py # Description: A simple Python program to make the computer act # like an alarm clock. Start it running

  • Mysql经典高逼格/命令行操作(速成)(推荐)

    由于要学习搭建服务器和数据库,所以最近开始自学sql语言了,至于写数据库就用比较基础的Mysql数据库了,虽然Mysql已经被互联网公司所淘汰掉了,他们都在使用Nosql,SQL server等sql语言,最终仍然决定从基础入手.经过简单的决定之后,就用逼格极高的cmd命令行来写了.Mysql数据库的安装方法这里就不给出详细的教程了,网上有好多安装教程可以自行选择安装. 1.第一步,我们要先在Mysql中建立一个库 以Mysql5.0为例,安装好以后从命令行登录Mysql: 在命令行输入:mys

  • Python3的介绍、安装和命令行的认识(推荐)

    PYTHON3介绍 Python是著名的"龟叔"Guido van Rossum在1989年圣诞节期间,为了打发无聊的圣诞节而编写的一个编程语言. Python就为我们提供了非常完善的基础代码库,覆盖了网络.文件.GUI.数据库.文本等大量内容,被形象地称作"内置电池(batteries included)".用Python开发,许多功能不必从零编写,直接使用现成的即可. 除了内置的库外,Python还有大量的第三方库,也就是别人开发的,供你直接使用的东西.当然,如

  • Jmeter命令行执行脚本如何设置动态参数

    最近看到在Linux上折腾jmeter的人越来越多,不过即使在windows上,jmeter的脚本我还是建议用命令行来执行(降低GUI模式带来的性能损耗,不过拿jmeter来做接口测试的无所谓).做性能测试的时候,有时候可能需要不停的去设置"线程组"那个界面的参数(例如线程数.循环次数.持续时间等),而每次修改-->保存-->cmd中运行总感觉麻烦,那就找一种方法可以在命令行中直接指定参数的值. 查看相关资料实操,可以发现JMeter的测试计划在运行Sampler之前会先加

  • python argparse命令行参数解析(推荐)

    argparse是python用于解析命令行参数和选项的标准模块. 很多时候,需要用到解析命令行参数的程序,目的是在终端窗口输入训练的参数和选项. argparse 模块可以让人轻松编写用户友好的命令行接口. 程序定义它需要的参数,然后 argparse 将弄清如何从 sys.argv 解析出那些参数. argparse 模块还会自动生成帮助和使用手册,并在用户给程序传入无效参数时报出错误信息. test.py # -*- coding: utf-8 -*- import argparse #

  • 关于命令行执行Python脚本的传参方式

    目录 命令行执行Python脚本的传参 应用场景 方式一 方式二 python-命令行传参sys.argv实际运用 argv获取参数 getopt模块 实例 实际场景运用 命令行执行Python脚本的传参 应用场景 在对ABAQUS进行二次开发时,需要将核心脚本的外部数据传递到脚本内部并执行 核心脚本在运行时,可以调用所传递的变量参数 命令行执行或者用户子程序执行 方式一 使用sys.args 简单示例 import sys def test_sys_args():     if len(sys

随机推荐