本文介绍AWK是一种文本处理语言,距今已经历了40多年的发展。它支持POSIX标准,有几种符合标准的实现,在2020年居然仍然有着大量拥趸,无论是用于简单的文本处理任务还是用于处理“大数据”。
恰逢最近GNU Awk 5.1发布,我们正好给了我们一个理由,通览AWK前景,GNU Awk已经达到的成果,以及如今AWK用在哪里了。
这门语言在1977年诞生于贝尔实验室。其名称来自其作者的名字缩写:Alfred Aho,Peter Weinberger和Brian Kernighan。AWK是核心的Unix工具,旨在做一件事:过滤和转换文本行。它通常用于解析日志文件中的字段,转换其他工具的输出以及计算单词和字段的出现次数。
AWK从输入中按行读取。每行数据都会和程序中的每个模式进行匹配,并为每个匹配的模式执行关联的动作。
AWK程序通常是直接从命令行执行的单行代码。例如,要从某个假定的Web服务器日志中计算GET请求的平均响应时间,可以输入如下代码:
$ awk '/GET/ {total += $6; n++} END {print total/n}' server.log
0.0186667
这意味着:对于与正则表达式/GET/匹配的所有行 ,将其响应时间(第六字段,即$6)加起来并统计行数;最后,打印出响应时间的算术平均值。
当前使用的AWK有三个主要版本,并且它们都符合POSIX标准(至少对于绝大多数用例而言,足够接近)。第一个是经典awk,它是Aho,Weinberger和Kernighan在他们的《 AWK编程语言》一书中描述的AWK版本。有时称为“新AWK”(nawk)或“一个真正的AWK”,现在托管在GitHub上。这是许多基于BSD的系统(包括macOS)上预先安装的版本(尽管macOS随附的版本已过时,需要升级)。
第二个是GNU Awk(gawk),它是迄今为止功能最强大,维护最活跃的版本。Gawk通常预先安装在Linux系统上,并且通常是默认的awk。使用Homebrew可以很容易地在macOS上安装,Gawk还提供Windows二进制文件。自1994年以来,Arnold Robbins一直是gawk的主要维护者,并继续推广该语言(他还为经典的awk版本做出了许多修复)。Gawk具有awk或POSIX标准中未提供的许多功能,包括新函数,联网功能,C扩展API,事件探查器和调试器以及最近的名称空间。
第三个通用版本是mawk,由Michael Brennan编写。它是Ubuntu和Debian Linux上的默认awk,并且仍然是AWK的最快版本,具有字节码编译器和更节省内存的值表示形式。(从4.0开始,Gawk还使用了字节码编译器,因此它现在已经接近mawk的速度。)
如果你想将AWK用于单行和基本文本处理,则上述任何方法都是不错的选择。如果你打算将其用于较大的脚本或程序,Gawk的功能使其成为明智的选择。
AWK的其他几种实现也具有不同的成熟度和维护级别。值得注意的有这些:嵌入式Linux环境中使用的尺寸优化的BusyBox版本,支持运行时访问Java语言的Java版以及我自己用Go编写的GoAWK(与POSIX兼容的版本)。三个主要的AWK和BusyBox版本都用C编写。
自LWN 涵盖 gawk 4.0发行以来,已经过去了将近10年。你可能会说“自2011年以来发生了很多变化”,但事实是AWK世界中的进展相对缓慢。我将在此介绍自4.0以来的重大功能,更多详细信息,你可以阅读完整的4.x和5.x 更改日志。Gawk 5.1.0已于一个月前(4月14日)发布。
面向用户的最大功能是在5.0中引入名称空间。大多数现代语言都有一些名称空间的概念,这可以使大型项目和库的发布更加容易,而不会造成名称冲突。Gawk 5.0以向后兼容的方式添加名称空间,从而允许开发人员创建库,例如此玩具数学库:
# area.awk
@namespace "area"
BEGIN {
pi = 3.14159 # namespaced "constant"
}
function circle(radius) {
return pi*radius*radius
要引用库中的变量或函数,请使用 namespaceC :: name语法,类似于C ++:
$ gawk -f area.awk -e 'BEGIN {print area :: pi,area :: circle(10)}'
3.14159 314.159
Robbins 认为 AWK缺少名称空间是它没有成为被广泛使用的编程语言的主要原因之一,而且gawk 5.0中的此功能可能有助于解决该问题。Robbins认为阻碍AWK的另一个主要问题是缺少良好的C扩展接口。Gawk的动态扩展界面在4.1中进行了彻底改进。它现在已具备定义良好的扩展API,并允许包装现有的C和C ++库,以便可以从AWK轻松调用它们。
以下代码片段出自用户手册中C代码包装器的示例,它的功能是用文件名和stat()系统调用的值填充AWK数组(字符串作为键的哈希表):
/*清空数组*/
clear_array(array);
/*填写数组*/
array_set(array,“name”,make_const_string(name,strlen(name),&tmp));
array_set_numeric(array,“dev”,sbuf-> st_dev);
array_set_numeric(array,“ino”,sbuf-> st_ino);
array_set_numeric(array,“mode”,sbuf-> st_mode);
4.2版本中的另一个更改(并在5.0中继续)是对源代码漂亮打印机的全面检查。Gawk的漂亮打印机可以将其用作标准化的AWK代码格式化程序,类似于Go的go fmt工具和Python的 Black格式化程序。例如,要漂亮地打印上面提到的 area.awk文件,使用如下命令:
$ gawk --pretty-print -f area.awk
@namespace "area"
BEGIN {
pi = 3.14159 # namespaced "constant"
}
function circle(radius)
{
return (pi * radius * radius)
你可能会质疑工具的选择:为什么“ BEGIN { ”没有像函数一样在“{ ”前加入换行符?(事实证明AWK语法不允许这样做。)为什么在函数前有两个空行并在return表达式前后加上括号?但是至少它是一致的,并且可能有助于避免代码风格的争论。
Gawk允许数量有限的运行时类型检查,并通过在4.2中添加typeof()函数进行扩展 。根据输入类型,typeof()返回一个字符串常量,例如“ string ”,“ number ”或“ array ”。这些功能对于递归遍历嵌套数组的每个项目非常重要(这是POSIX AWK无法做到的)。
在4.2版中,gawk还使用@/foo/语法将正则表达式常量作为一等数据类型 。以前,你无法将正则表达式常量存储在变量中。使用typeof(@/foo/)判断其数据类型将返回字符串“ regexp ”。gawk 4.2在Linux系统的性能方面进行了重大改进,它将尽量使用fwrite_unlocked()来提升性能。由于gawk是单线程的,因此可以使用无锁的stdio函数,从而使原始输出速度提高了7-18%(使用原始输出的命令是这样的gawk'{print}')。
《GNU Awk用户指南》是一份详尽的参考资料,并在4.1版和5.x版中进行了实质性更新,包括新示例,摘要部分和练习,以及一些主要的副本编辑。
最后(也是最不重要的一点),我发现4.0版本中一个有趣的细微变化是对sub()和 gsub()中反斜杠的还原处理。Robbins写道:
sub()和gsub()中反斜杠的默认处理已恢复为3.1。我认为破坏兼容性是很愚蠢的,即使是为了符合标准。
该sub 和gsub函数是核心的正则表达式替换功能,甚至一个涉及复杂处理反斜杠的小“修复”就能破坏已有的代码:
当4.0.0版发布时,gawk维护者将POSIX规则设置为默认规则,打破了十多年的向后兼容性。不用说,这是个坏主意,从4.0.1版开始,gawk恢复了其历史行为,并且仅在给出--posix时才遵循POSIX规则。
Robbins进行最初的更新时可能判断不足,但是很明显他很重视向后兼容性。尤其是对于像gawk这样的流行工具,有时继续打破规范比改变某些东西的工作方式更好。
AWK是否仍然有用这个问题,就像在问空气是否有用一样:你可能看不到它,但周围无处不在。许多Linux管理员和DevOps工程师使用它来转换数据或通过日志文件诊断问题。几乎所有基于Unix的计算机上都安装了AWK版本。除了临时使用外,许多大型开源项目还在其构建或文档工具中使用AWK。仅举几个例子:Linux内核在x86工具中使用它来检查 和重新格式化 objdump文件,Neovim使用它来生成文档,而FFmpeg使用它来进行构建和测试。
即使人们不再想看到AWK,AWK构建脚本也很难被杀死:2018年,LWN 写了一篇文章,是关于GCC贡献者想要用Python替换AWK脚本来生成其选项解析代码。这个建议在当时有一些支持,但显然没有人主动要求做实际的移植,现在AWK脚本依然存在。
Robbins在他的2018年论文中主张将AWK(特别是gawk)用作“系统编程语言”,在这种情况下,其含义是用于编写较大的工具和程序的语言。他概述了他认为尚未流行的原因,但Kernighan“ 并非100%确信 ”缺乏扩展机制是AWK不被大型程序广泛使用的主要原因。他建议,这可能是由于缺乏对访问系统调用等内容的内置支持。但是,这些都没有阻止几个人开发更大的工具:Robbins自己编写的TexiWeb Jr.识字编程工具(1300行AWK),Werner Stoop的d.awk工具用于从源码中备注的MarkDown生成文档, Translate Shell是一个6000行AWK工具,可为基于云的翻译API提供相当强大的命令行界面。
过去几年中,有几位开发人员写过关于在其“大数据”工具包中使用AWK的信息,它比重型分布式计算系统(如Spark和Hadoop)简单得多(有时更快)。Nick Strayer 撰写了 有关使用AWK和R解析多个内核中25 TB数据的文章。其他大数据示例包括Adam Drake 写的标题颇为诱人的文章:“命令行工具可以比Hadoop集群快235倍”,以及Brendan O'Connor的“ 不要使用MAWK,AWK是最快,最优雅的大数据处理语言”。
总而言之,在临时文本修改,构建工具,“系统编程”和大数据处理之间(更不用说文本模式的第一人称射击游戏),AWK似乎在2020年还活得很好。
https://lwn.net/SubscriberLink/820829/5bf9bf8bb9d6f2bf/
——END——