最近偶然看到的一篇linuxjishu/13830.html target=_blank class=infotextkey>awk命令教程,通过详细的awk实例介绍了awk命令的用法,包括awk常用选项,awk命令中BEGIN与END语块的用法,以及awk在数据格式化时的一些技巧。
总之,是有关awk命令用法的最全面的一篇教程了,喜欢awk命令的朋友,建议不要错过。
awk可应用于各种计算和数据处理任务。
1.1 起步
有用的awk程序往往很简短,仅仅一两行。假设你有一个名为 emp.data 的文件,其中包含员工的姓名、薪资(美元/小时)以及小时数,一个员工一行数据,如下所示:
现在你想打印出工作时间超过零小时的员工的姓名和工资(薪资乘以时间)。这种任务对于awk来说就是小菜一碟。输入此命令行:
应该会得到如下输出:
Kathy 40
Mark 100
Mary 121
Susie 76.5
该命令行告诉系统执行引号内的awk程序,从输入文件 emp.data 获取程序所需的数据。引号内的部分是个完整的awk程序,包含单个模式-动作语句。模式 $3>0 用于匹配第三列大于0的输入行,动作:
打印每个匹配行的第一个字段以及第二第三字段的乘积。
如果你想打印出还没工作过的员工的姓名,则输入命令行::
这里,模式 $3 == 0 匹配第三个字段等于0的行,动作:
打印该行的第一个字段。
当你阅读本书时,应该尝试执行与修改示例程序。大多数程序都很简短,所以你能快速理解awk是如何工作的。在Unix系统上,以上两个事务在终端里看起来是这样的:
行首的 $ 是系统提示符,也许在你的机器上不一样。
AWK程序的结构
上述的命令行中,引号之间的部分是awk编程语言写就的程序。本章中的每个awk程序都是一个或多个模式-动作语句的序列:
pattern { action }
pattern { action }
...
awk的基本操作是一行一行地扫描输入,搜索匹配任意程序中模式的行。词语“匹配”的准确意义是视具体的模式而言,对于模式 $3 >0 来说,意思是“条件为真”。
每个模式依次测试每个输入行。对于匹配到行的模式,其对应的动作(也许包含多步)得到执行,然后读取下一行并继续匹配,直到所有的输入读取完毕。
上面的程序都是模式与动作的典型示例。:
是单个模式-动作语句;对于第三个字段为0的每行,打印其第一个字段。
模式-动作语句中的模式或动作(但不是同时两者)都可以省略。如果某个模式没有动作,例如::
$3 == 0
那么模式匹配到的每一行(即,对于该行,条件为真)都会被打印出来。该程序会打印 emp.data 文件中第三个字段为0的两行
Beth 4.00 0
Dan 3.75 0
如果有个没有模式的动作,例如::
{ print $1 }
那么这种情况下的动作会打印每个输入行的第一列。
由于模式和动作两者任一都是可选的,所以需要使用大括号包围动作以区分于其他模式。
执行AWK程序
执行awk程序的方式有多种。你可以输入如下形式的命令行:
从而在每个指定的输入文件上执行这个program。例如,输入::
打印file1和file2文件中第三个字段为0的每一行的第一个字段。
你可以省略命令行中的输入文件,仅输入::
这种情况下,awk会将program应用于你在终端中接着输入的任意数据行,直到你输入一个文件结束信号(Unix系统上为control-d)。
如下是Unix系统的一个会话示例:
$ awk ‘$3 == 0 { print $1 }’
Beth 4.00 0
Beth
Dan 3.75 0
Dan
Kathy 3.75 10
Kathy 3.75 0
Kathy
...
加粗的字符是计算机打印的。
这个动作非常便于尝试awk:输入你的程序,然后输入数据,观察发生了什么。我们再次鼓励你尝试这些示例并进行改动。
注意命令行中的程序是用单引号包围着的。这会防止shell解释程序中 $ 这样的字符,也允许程序的长度超过一行。
当程序比较短小(几行的长度)的时候,这种约定会很方便。
然而,如果程序较长,将程序写到一个单独的文件中会更加方便。
假设存在程序 progfile ,输入命令行::
其中 -f 选项指示awk从指定文件中获取程序。可以使用任意文件名替换 progfile 。
错误
如果你的awk程序存在错误,awk会给你一段诊断信息。例如,如果你打错了大括号,如下所示::
得到如下信息:
“Syntax error”意味着在 >>> <<< 标记的地方检测到语法错误。“Bailing out”意味着没有试图恢复。有时你会得到更多的帮助-关于错误是什么,比如大括号或括弧不匹配。
因为存在句法错误,awk就不会尝试执行这个程序。然而,有些错误,直到你的程序被执行才会检测出来。例如,如果你试图用零去除某个数,awk会在这个除法的地方停止处理并报告输入行的行号以及在程序中的行号(这话是什么意思?难道输入行的行号是忽略空行后的行号?)。
1.2 简单输出
这一节接下来的部分包含了一些短小,典型的awk程序,基于操纵上文中提到的 emp.data 文件. 我们会简单的解释程序在做什么,但这些例子主要是为了介绍 awk 中常见的一些简单有用的操作 – 打印字段, 选择输入, 转换数据. 我们并 没有展现 awk 程序能做的所有事情, 也并不打算深入的去探讨例子中的一些细节. 但在你读完这一节之后, 你将能够完成一些简单的任务, 并且你将发现在阅读后 面章节的时候会变的容易的多.
我们通常只会列出程序部分, 而不是整个命令行. 在任何情况下, 程序都可以用 引号包含起来放到 awk 命令的地一个参数中运行, 就像上文中展示的那样, 或者 把它放到一个文件中使用 awk 的 -f 参数调用它.
在 awk 中仅仅只有两种数据类型: 数值 和 字符构成的字符串. emp.data 是 一个包含这类信息的典型文件 – 混合了被空格和(或)制表符分割的数字和词语.
Awk 程序一次从输入文件的中读取一行内容并把它分割成一个个字段, 通常默认 情况下, 一个字段是一个不包含任何空格或制表符的连续字符序列。
当前输入的 行中的地一个字段被称做 $1, 第二个是 $2, 以此类推. 整个行的内容被定 义为 $0. 每一行的字段数量可以不同.
通常, 我们要做的仅仅只是打印出每一行中的某些字段, 也许还要做一些计算. 这一节的程序基本上都是这种形式.
打印每一行
如果一个动作没有任何模式, 这个动作会对所有输入的行进行操作. print 语 句用来打印(输出)当前输入的行, 所以程序
{ print }
会输出所有输入的内容到标准输出. 由于 $0 表示整行,
{ print $0 }
也会做一样的事情.
打印特定字段
使用一个 print 语句可以在同一行中输出不止一个字段. 下面的程序输出了每 行输入中的第一和第三个字段
{ print $1, $3 }
使用 emp.data 作为输入, 它将会得到
Beth 0
Dan 0
Kathy 10
Mark 20
Mary 22
Susie 18
在 print 语句中被逗号分割的表达式, 在默认情况下他们将会用一个空格分割 来输出. 每一行 print 生成的内容都会以一个换行符作为结束. 但这些默认行 为都可以自定义; 我们将在第二章中介绍具体的方法.
NF, 字段数量
很显然你可能会发现你总是需要通过 $1, $2 这样来指定不同的字段, 但任何表 达式都可以使用在$之后来表达一个字段的序号; 表达式会被求值并用于表示字段 序号. Awk会对当前输入的行有多少个字段进行计数, 并且将当前行的字段数量存 储在一个内建的称作 NF 的变量中. 因此, 下面的程序
{ print NF, $1, $NF }
会依次打印出每一行的字段数量, 第一个字段的值, 最后一个字段的值.
计算和打印
你也可以对字段的值进行计算后再打印出来. 下面的程序
{ print $1, $2 * $3 }
是一个典型的例子. 它会打印出姓名和员工的合计支出(以小时计算):
Beth 0
Dan 0
Kathy 40
Mark 100
Mary 121
Susie 76.5
我们马上就会学到怎么让这个输出看起来更漂亮.
打印行号
Awk提供了另一个内建变量, 叫做 NR, 它会存储当前已经读取了多少行的计数. 我们可以使用 NR 和 $0 给 emp.data 的没一行加上行号:
在输出中添加内容
你当然也可以在字段中间或者计算的值中间打印输出想要的内容:
在打印语句中, 双引号内的文字将会在字段和计算的值中插入输出.
1.3 高级输出
print 语句可用于快速而简单的输出。若要严格按照你所想的格式化输出,则需要使用 printf 语句。正如我将在2.4节所见, printf 几乎可以产生任何形式的输出,但在本节中,我们仅展示其部分功能。
字段排队
printf 语句的形式如下::
printf(format, value1, value2, ..., valuen)
其中 format 是字符串,包含要逐字打印的文本,穿插着 format 之后的每个值该如何打印的规格(specification)。一个规格是一个 % 符,后面跟着一些字符,用来控制一个 value 的格式。第一个规格说明如何打印 value1 ,第二个说明如何打印 value2 ,... 。因此,有多少 value 要打印,在 format 中就要有多少个 % 规格。
这里有个程序使用 printf 打印每位员工的总薪酬::
printf 语句中的规格字符串包含两个 % 规格。第一个是 %s ,说明以字符串的方式打印第一个值 $1 。第二个是 %.2f ,说明以数字的方式打印第二个值 $2*$3 ,并保留小数点后面两位。规格字符串中其他东西,包括美元符号,仅逐字打印。字符串尾部的 n 代表开始新的一行,使得后续输出将从下一行开始。以 emp.data 为输入,该程序产生:
total pay for Beth is $0.00
total pay for Dan is $0.00
total pay for Kathy is $40.00
total pay for Mark is $100.00
total pay for Mary is $121.00
total pay for Susie is $76.50
printf 不会自动产生空格或者新的行,必须是你自己来创建,所以不要忘了 n 。
另一个程序是打印每位员工的姓名与薪酬:
第一个规格 %-8s 将一个姓名以字符串形式在8个字符宽度的字段中左对齐输出。第二个规格 %6.2f 将薪酬以数字的形式,保留小数点后两位,在6个字符宽度的字段中输出。
Beth $ 0.00
Dan $ 0.00
Kathy $ 40.00
Mark $100.00
Mary $121.00
Susie $ 76.50
之后我们将展示更多的 printf 示例。一切精彩尽在2.4小节。
排序输出
假设你想打印每位员工的所有数据,包括他或她的薪酬,并以薪酬递增的方式进行排序输出。最简单的方式是使用awk将每位员工的总薪酬置于其记录之前,然后利用一个排序程序来处理awk的输出。Unix上,命令行如下:
将awk的输出通过管道传给 sort 命令,输出为:
1.4 选择
Awk的模式适合用于为进一步的处理从输入中选择相关的数据行。由于不带动作的模式会打印所有匹配模式的行,所以很多awk程序仅包含一个模式。本节将给出一些有用的模式示例。
通过对比选择
这个程序使用一个对比模式来选择每小时赚5美元或更多的员工记录,也就是,第二个字段大于等于5的行::
通过计算选择
程序
打印出总薪资超过50美元的员工的薪酬。
通过文本内容选择
除了数值测试,你还可以选择包含特定单词或短语的输入行。这个程序会打印所有第一个字段为 Susie 的行::
$1 == "Susie"
操作符 == 用于测试相等性。你也可以使用称为 正则表达式 的模式查找包含任意字母组合,单词或短语的文本。这个程序打印任意位置包含 Susie 的行::
正则表达式可用于指定复杂的多的模式;2.1节将会有全面的论述。
模式组合
可以使用括号和逻辑操作符与 && , 或 || , 以及非 ! 对模式进行组合。程序:
会打印 $2 (第二个字段) 大于等于 4 或者 $3 (第三个字段) 大于等于 20 的行::
Beth 4.00 0
kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18
两个条件都满足的行仅打印一次。与如下包含两个模式程序相比::
$2 >= 4
$3 >= 20
如果某个输入行两个条件都满足,这个程序会打印它两遍::
Beth 4.00 0
Kathy 4.00 10
Mark 5.00 20
Mark 5.00 20
Mary 5.50 22
Mary 5.50 22
Susie 4.25 18
注意如下程序:
会打印极不满足 $2 小于4也不满足 $3 小于20的行;这个条件与上面第一个模式组合等价,虽然也许可读性差了点。
数据验证
实际的数据中总是会存在错误的。在数据验证-检查数据的值是否合理以及格式是否正确-方面,Awk是个优秀的工具。