GAWK 学习:AWK 语言基础

xiaoxiao2021-02-28  45

本文结构为:

awk与gawk的关系gawk 的特性和优点gawk使用规则与运行方式gawk字段、变量、操作符gawk以/etc/passwd为操作文件的常用用法示例

GNU AWK (GAWK) 是AWK 编程语言的开放源代码实现,可用于所有的 UNIX® 系统。AWK 语言是一种 UNIX 备用工具,它是一种功能强大的文本操作和模式匹配语言,特别适用于进行信息检索,这使得它非常适合用于当今的数据库驱动的应用程序。因为它集成于 UNIX 环境,所以可以设计、构建和快速地执行完整的工作程序,并且立即就能得到结果。

AWK 语言

AWK 是GAWK编程语言本身的名称,它编写于 1977 年。其名称是三个主要作者的姓的首字母缩写:Drs. A. Aho、P. Weinberger 和 B. Kernighan。

因为 AWK 是一种文本处理和模式匹配语言,所以它通常称为数据驱动的 语言,程序语句描述需要进行匹配和处理的输入数据,而不是程序操作步骤的序列,在许多语言中都是这样的。AWK 程序在其输入数据中搜索包含模式的记录、对该记录执行指定的操作,直到程序到达输入的末尾。AWK 程序擅长于处理数据库和表型数据,如从多个数据集中提取一些列、建立报表或分析数据。事实上,AWK 适合于编写短小的、一次性程序,以执行一些灵活的文本处理,而使用其他的语言则可能成本较高。另外,作为一种功能强大的工具,AWK 常常在命令行中使用或与管道一起使用。

与 Perl(它起源于 AWK)一样,AWK 是一种解释性语言,所以 AWK 程序通常不需要进行编译。相反,在运行时将程序脚本传递给 AWK 解释器。

GAWK 的特性和优点

GAWK 具有下列独特的特性和优点:

在所有主要的 UNIX 平台以及其他操作系统中都可以使用它,包括 Mac OS X 和 Microsoft® Windows®。它是可移植操作系统接口 (POSIX) 兼容的,并且包含 1992 POSIX 标准中的所有特性。它没有预定义的内存限制。可以使用一些新的内置函数和变量。它包含一些特殊的 regexp 操作符。记录分隔符中可以包含 regexp 操作符。可以使用特殊文件支持来访问标准的 UNIX 流。可以使用 Lint 检查。在缺省情况下,它使用扩展的正则表达式。它支持无限制的行长度和连续使用反斜杠字符 ()。它具有更好的、更具描述性的错误消息。它包含一些 TCP/IP 网络函数。

AWK 输入文件的组成部分

AWK 针对文本输入进行操作,而该文本可以是一个文件或标准输入流,它对文本进行分类以得到记录和字段。AWK 记录 是单个的、连续长度的输入数据,是 AWK 的操作对象。记录由记录分隔符 限定,记录分隔符是一个字符串,并且定义为 RS 变量。在缺省情况下,RS 的值设置为换行符,所以 AWK 的缺省行为是将整行输入作为记录连续地读取记录,直到到达输入的末尾

图 1. 将 AWK 输入数据划分为记录 还可以将每个记录进一步分解为称作字段 的单独的块。与记录一样,字段也受到限定。缺省的字段分隔符 是任意数量的空白字符,包括制表符和空格字符。所以在缺省情况下,将输入行进一步分解为单独的单词(由空白字符分隔的任何字符组)。 您可以通过字段编号来引用记录中的字段,从 1 开始。您可以使用字段编号或特殊的变量 NF 来调用每个记录中的最后一个字段,该变量包含当前记录中字段的个数。 您可以通过字段编号 0 来引用整个记录,包括所有的字段和字段分隔符。图 2 显示了这样一个字段的内容,即将 AWK 输入数据的单个记录分解为复合字段。 图 2. AWK 记录中的字段

了解规则

AWK 程序由规则 组成,它们是一些模式,后面跟着由换行分隔的操作。当 AWK 执行一条规则时,它在输入记录中搜索给定模式的匹配项,然后对这些记录执行给定的操作: /pattern/ { action } 您可以在规则中省略模式或操作。 操作由 AWK 语句组成,使用分号 (;) 来进行分隔。您还可以在同一行内容中提供多个规则,但是您必须使用分号对它们进行分隔。当规则中仅包含一项操作时,将对输入中的每条记录执行该操作。当规则中仅包含一个模式时,将打印出匹配该模式的所有记录。 空模式 // 匹配空字符,这等价于规则中不包含任何模式。然而,当给定空操作 {} 时,这并不等价于没有任何操作。空操作表示不进行任何操作(因此,不打印出该记录)。

一些基本的操作

常用的 GAWK 语句

运行 GAWK 程序 GAWK 接受两个输入文件,即包含 AWK 程序本身的命令文件和要进行处理的数据文件。例如我们有以下的文本内容,保存为sample:

Heigh-ho! sing, heigh-ho! unto the green holly: Most friendship is feigning, most loving mere folly: Then, heigh-ho, the holly!

运行 GAWK 程序的不同方法

您可以使用四种方法来运行 GAWK 程序:在命令行中、作为筛选器、从文件或作为脚本。

1.在命令行中 不需要将命令保存到文件中:您可以将它们作为参数传递。这是运行 GAWK 单行程序 的常用方法。 其格式是:

awk 'program' filespec

尝试使用下面的单行程序,以打印出示例文件中的每行内容:

$ awk '{ print }' sample Heigh-ho! sing, heigh-ho! unto the green holly: Most friendship is feigning, most loving mere folly: Then, heigh-ho, the holly! $

这个程序读取示例文件,并打印出其中的每个记录,换句话说,它仅仅逐字地输出整个文件。 因为 GAWK 可以接受多个输入文件,所以如果您指定多个输入文件作为参数,该程序可以将它们连接起来。 多行内容的单行程序

$ awk 'BEGIN { print "Beginning of file."; print "------------------" }, // END { print "------------"; print "End of file."}' sample Beginning of file ----------------- Heigh-ho! sing, heigh-ho! unto the green holly: Most friendship is feigning, most loving mere folly: Then, heigh-ho, the holly! ------------ End of file. $

除了任意长度输入程序的实用性之外,在命令行中运行 AWK 程序需要注意一点:必须小心地使用引号!AWK 中的变量,如 $1,可以表示字段,这一点将在下一个部分中进行描述,在许多 Shell 中,这些字段是特殊的变量,而这些变量将转换为在命令行中给定的参数。

2.作为筛选器 AWK 通常用作管道中的文本筛选器。在这种情况下,GAWK 从标准输入读取数据: awk ‘program’ 尝试使用示例文件测试下面的命令:

$ cat sample | awk '{ print }' Heigh-ho! sing, heigh-ho! unto the green holly: Most friendship is feigning, most loving mere folly: Then, heigh-ho, the holly! $

3.来自文件 在实际中,通常只在命令行中运行单行程序。大型的 GAWK 程序通常保存在文件中。可以使用 -f 选项指定程序文件。 所以,假设一个名为 progfile 的文件中包含下面的内容: { print } 当您运行下面的命令时,将与在在命令行中运行 awk 得到相同的结果:

$ awk -f progfile sample Heigh-ho! sing, heigh-ho! unto the green holly: Most friendship is feigning, most loving mere folly: Then, heigh-ho, the holly! $

请注意,如果程序放在文件中,那么就不需要在 Shell中使用引号将内容括起来。 4.作为脚本 另一种运行 GAWK 程序的方法是将其放到文件中,并使用 shebang 命令(井号加感叹号)将该文件变成可执行的脚本,如果您的 Shell 支持这种方法。 将程序放到一个名为 awkat 的 GAWK 脚本文件中,使其具有执行权限,并尝试使用示例文件运行该脚本,如下 将简单的程序作为可执行文件运行

$ cat > awkat#!/usr/bin/awk -f{ print }Ctrl-d $ chmod u+x awkat $ ./awkat sample Heigh-ho! sing, heigh-ho! unto the green holly: Most friendship is feigning, most loving mere folly: Then, heigh-ho, the holly! $

操作记录和字段

当 GAWK 读入一个记录时,它会将该记录中所有的字段存储到变量中。您可以通过使用 $ 加上字段编号来引用每个字段,所以 $1 引用第 1 个字段,$2 引用第 2 个字段,依此类推,直到记录中的最后一个字段。 如输入文件的组成部分中所述,您可以使用 $0 来引用整个记录,其中包括所有的字段和字段分隔符。这是许多命令的缺省值。所以例如,正如前面使用过的 print,它等价于 print $0,这两个命令都将打印出当前整个记录。

打印特定的字段 要输出某个特定的字段,可以将该字段的名称作为参数提供给 print。下面的命令尝试打印示例文件中每个记录的第 1 个字段:

$ awk ' { print $1 } ' sample Heigh-ho! Most Then, $

您可以在 print 语句中提供多个字段,并且它们可以使用任意的顺序:

$ awk ' { print $7, $3, $1 } ' sample holly: heigh-ho! Heigh-ho! mere is Most the Then, $

请注意,有些行中没有第 7 个字段,在这种情况下,将不打印任何内容。 当使用逗号进行分隔时,这些字段在输出时将使用空格隔开。您可以省略逗号,以便将它们连接起来。要打印第 7 个和第 8 个字段,并将它们连接起来,可以使用下面的命令:

$ awk ' { print $7 $8 } ' sample holly: merefolly: $

您可以将使用引号括起来的文本和字段组合在一起。尝试下面的命令:

$ awk ' { print "Field 2: " $2 } ' sample Field 2: sing, Field 2: friendship Field 2: heigh-ho, $

您已经了解了处理字段和记录的强大功能,使用 AWK,很容易对表型数据进行解析、操作,并使用一些简单的命令重新进行格式化。您可以使用 Shell 重定向机制将经过重新格式化的输出定向到一个新的文件,或使用管道进行传输。 如果作为筛选器使用,那么可以将这种功能与其他的命令联合使用,这是非常有价值的。例如,下面的命令修改了日期的缺省输出,以便以日 月,年 的格式进行输出:

$ date|awk '{print $3 " " $2 ", " $6}' 29 Nov, 2006 $

更改字段分隔符 缺省的字段分隔符为空格,即任意个数的空格或制表符,您可以对其进行更改。字段分隔符的值保存在 FS 变量中。与 AWK 中的其他变量一样,可以在程序中的任何位置对其进行重新定义。要对整个文件使用不同的字段分隔符,可以在 BEGIN 语句重新定义它。 使用感叹号 (!) 作为字段分隔符打印示例数据的第 1 个字段:

$ awk ' BEGIN { FS = "!" } { print $1 } ' sample Heigh-ho Most friendship is feigning, most loving mere folly: Then, heigh-ho, the holly

请注意在打印第 2 个和第 3 个字段时的差别:

$ awk ' BEGIN { FS = "!" } { print $2 } ' sample sing, heigh-ho $ awk ' BEGIN { FS = "!" } { print $3 } ' sample unto the green holly:

尝试将输出中的字段与图 4 中列出的字段进行比较。 但是字段分隔符并不一定必须是单个字符。可以使用一个短语:

$ awk ' BEGIN { FS = "Heigh-ho" } { print $2 } ' sample ! sing, heigh-ho! unto the green holly:

在 GAWK 中,字段分隔符可以是任何正则表达式。要使每个输入字符成为一个独立的字段,可以让 FS 的值为空。 算出大写字母的总数。上面的示例仅在整个文件中匹配一个分隔符,而下面的示例将匹配短语,无论大小写:

$ awk ' BEGIN { FS = "[Hh]eigh-ho" } { print $2 } ' sample ! sing, , the holly!

通过在命令行中使用引号将其括起来并作为 -F 选项的参数,也可以更改字段分隔符:

$ awk -F "," ' { print $2 } ' sample heigh-ho! unto the green holly: most loving mere folly: heigh-ho

使用这种功能,很容易创建可以对文件进行解析的单行程序,如 /etc/passwd,其中的字段由冒号 (:) 进行分隔。您可以轻松地从中提取完整的用户名称列表,例如:

$ awk -F ":" ' { print $5 } ' /etc/passwd

更改记录分隔符 与字段分隔符一样,您可以将记录分隔符的缺省值(换行符)更改为所需的任何内容。其当前值保存在 RS 变量中。 将记录分隔符更改为逗号,并对示例文件使用它:

$ awk ' BEGIN { RS = "," } //' sample Heigh-ho! sing heigh-ho! unto the green holly: Most friendship is feigning most loving mere folly: Then heigh-ho the holly!

更多 GAWK 变量

NF 变量包含当前记录中字段的个数。使用 NF 可以引用其数值,而使用 $NF 则表示引用实际字段本身的内容。所以,如果记录有 100 个字段,print NF 将输出整数 100,而 print $100 则与 print $NF 输出相同的结果,都是该记录中最后一个字段的内容。 NR 变量包含当前的记录个数。当读取到第 1 个记录时,其值为 1,当读取到第 2 个记录时,其值增为 2,依此类推。在 END 模式中使用它,以便输出输入中的行数: $ awk 'END { print "Input contains " NR " lines." }' sample Input contains 3 lines.

注意:如果上面的 print 语句位于 BEGIN 模式中,那么该程序将报告其输入包含 0 行内容,因为在执行时 NR 的值为 0,因为此时尚未读入任何输入记录。 使用 $NR 打印相对于当前记录个数的字段:

$ awk '{ print NR, $NR }' sample 1 Heigh-ho! 2 friendship 3 the

再来研究一下图 4,可以看到第 1 个记录中的字段 1 的值、第 2 个记录中的字段 2 的值,以及最后一个记录中字段 3 的值。将其与您的程序输出进行比较。 尝试列出每个记录中字段的个数,以及最后一个字段的值:

$ awk ' { print "Record " NR " has " NF " fields and ends with " $NF}' sample Record 1 has 7 fields and ends with holly: Record 2 has 8 fields and ends with folly: Record 3 has 4 fields and ends with holly!

通常可以使用大量特殊的 GAWK 变量,下图为常用: GAWK 布尔操作符

&& 逻辑“与”操作符 || 逻辑“或”操作符 ! “非”操作符

尝试仅输出示例数据中匹配 regexpin、the 和 to 的那些记录的第 4 个字段:

$ awk '/in/ && /the/ && /to/ {print $4}' sample unto

现在,打印示例数据中不包含 holly 或确实 包含 most 的那些记录,同时将输出记录分隔符更改为连字符:

$ awk 'BEGIN { OFS="-" } ! /holly/ || /most/ { print $1, $2 }' sample

Most-friendship

使用范围模式 在两个模式之间使用逗号,可以指定一个范围,这表示匹配位于这两种模式之间和模式本身的所有文本。与其他的搜索不同,范围模式在匹配文本时可以跨越不同的记录。它输出包含匹配项部分内容的完整的记录。例如,搜索从 Heigh 到 folly 的范围,将输出前两个记录的文本:

$ awk '/Heigh/,/folly/' sample Heigh-ho! sing, heigh-ho! unto the green holly: Most friendship is feigning, most loving mere folly:

输出匹配的整段内容 如果您使用换行符作为字段分隔符、使用空字符串作为记录分隔符,那么 AWK 会将整段内容作为一个记录。这样使其成为了“段落 grep”,它将输出匹配搜索的整段内容。对于包含大量文本的文件,该功能可能非常有价值。 重定义分隔符,然后在示例文件中搜索 green。如果不进行重新定义,该搜索仅输出第 1 个记录(即第 1 行)。但是,因为整个示例文件中只有一个段落,所以该匹配将输出整个文件本身:

$ awk 'BEGIN { FS="\n"; RS="" } /green/' sample Heigh-ho! sing, heigh-ho! unto the green holly: Most friendship is feigning, most loving mere folly: Then, heigh-ho, the holly!

一些使用示例

gawk -F: '{print $1}' /etc/passwd #按照指定的子段分隔符分割字符并输出分割后的第一个子段 gawk 'BEGIN{FS=":";OFS="--"}{print $1,$2,$3}' /etc/passwd #按照指定的子段分隔符分割字符,在输出的时候通过指定的子段进行连接 gawk 'BEGIN{FIELDIDTHS="2 2 2"}{print $1,$2,$3}' /etc/passwd #根据子段宽度来计算子段进行分割 gawk 'BEGIN{FS="\n";RS=""}{print $1,$3}' gawktest #将每行当作一个子段,然后把空行当作分隔符 gawk 'BEGIN{FS=":";OFS=":"}{print $1,$NF}END{print ARGC}' /etc/passwd #NF指定数据行中的最后一个数据字段 gawk 'BEGIN{FS="."}{print $1,"FNR="FNR,"NR="NR}END{print "there were",NR,"records"}' halo halo1 #FNR变量包含当前处理的数据文件的总行数,但是NR包含所有处理过的数据的总行数,如果文件数为一个的话那这两个数值就是相等的,但是多于一个文件时FNR的值会在每一个文件被处理时被重置 gawk ''BEGIN{halo="halooooo";print halo}' #变量赋值并且使用; #通过在调用脚本时传递参数,但是问题时BEGIN部分并不识别,所以需要这么搞: gawk -v n=3 -f script file ##################使用模式 gawk 'BEGIN{FS="."} /23/{print $1} END{print "nimeide"}' halo #输出文本中匹配指定模式的行 gawk 'BEGIN {FS=":"} $1 ~ /root/{print $1,$NF}' /etc/passwd root /bin/bash #在第一个数据字段中查找匹配root的行然后打印出第一个和最后一个字段(也可以使用!~来进行排除) gawk 'BEGIN {FS=":"} $NF ~ /\/bin\/bash/{print $1,$NF}' /etc/passwd #最后一个字段匹配/bin/bash的行 gawk 'BEGIN {FS=":"} $4 == 0{print $1,$NF}' /etc/passwd #匹配所有属于root用户组的用户
转载请注明原文地址: https://www.6miu.com/read-35548.html

最新回复(0)