本节介绍下awk命令的用法,无论你是刚接触awk命令的初学者,还是awk命令已上手很多的高手,这篇awk基础教程都可以带你走入awk的奇妙世界。 awk是一种便于使用且表达能力强的程序设计语言,可应用于各种计算和数据处理任务。 1.1 起步
有用的awk程序往往很简短,仅仅一两行。
awk '$3 >0 { print $1, $2 * $3 }' emp.data
输出:
引号内的部分是个完整的awk程序,包含单个模式-动作语句。
{ print $1, $2 * $3 }
打印每个匹配行的第一个字段以及第二第三字段的乘积。
如果打印出还没工作过的员工的姓名,则输入命令行::
awk '$3 == 0 { print $1 }' emp.data
这里,模式 $3 == 0 匹配第三个字段等于0的行,动作:
{ print $1 }
打印该行的第一个字段。
大多数awk程序都很简短,所以能快速理解awk是如何工作的。
$ awk ‘$3 > 0 { print $1, $2 * $3 }’ emp.data
Kathy 40 Mark 100 Mary 121 Susie 76.5 $ awk ‘$3 == 0 { print $1 }’ emp.data Beth Dan $ 行首的 $ 是系统提示符,也许在你的机器上不一样。 2、AWK程序的结构
上述的命令行中,引号之间的部分是awk编程语言写就的程序。本章中的每个awk程序都是一个或多个模式-动作语句的序列: 每个模式依次测试每个输入行。对于匹配到行的模式,其对应的动作(也许包含多步)得到执行,然后读取下一行并继续匹配,直到所有的输入读取完毕。
上面的程序都是模式与动作的典型示例。
$3 == 0 { print $1 }
是单个模式-动作语句;对于第三个字段为0的每行,打印其第一个字段。
模式-动作语句中的模式或动作(但不是同时两者)都可以省略。如果某个模式没有动作,例如: 由于模式和动作两者任一都是可选的,所以需要使用大括号包围动作以区分于其他模式。 3、执行AWK程序
执行awk程序的方式有多种。你可以输入如下形式的命令行::
awk 'program' input files
从而在每个指定的输入文件上执行这个program。例如,你可以输入::
awk '$3 == 0 { print $1 }' file1 file2
打印file1和file2文件中第三个字段为0的每一行的第一个字段。
可以省略命令行中的输入文件,仅输入::
awk 'program'
这种情况下,awk会将program应用于你在终端中接着输入的任意数据行,直到你输入一个文件结束信号(Unix系统上为control-d)。如下是Unix系统的一个会话示例:
$ awk ‘$3 == 0 { print $1 }’
Dan 3.75 0
Kathy 3.75 10
... 加粗的字符是计算机打印的。 这个动作非常便于尝试awk:输入你的程序,然后输入数据,观察发生了什么。我们再次鼓励你尝试这些示例并进行改动。 注意命令行中的程序是用单引号包围着的。这会防止shell解释程序中 $ 这样的字符,也允许程序的长度超过一行。
当程序比较短小(几行的长度)的时候,这种约定会很方便。然而,如果程序较长,将程序写到一个单独的文件中会更加方便。假设存在程序 progfile ,输入命令行::
awk -f progfile optional list of input files
其中 -f 选项指示awk从指定文件中获取程序。可以使用任意文件名替换 progfile 。 错误
如果你的awk程序存在错误,awk会给你一段诊断信息。例如,如果你打错了大括号,如下所示::
awk '$3 == 0 [ print $1 }' emp.data
你会得到如下信息:
awk: syntax error at source line 1
context is $3 == 0 >>> [ <<< extra } missing ] awk: bailing out at source line 1 “Syntax error”意味着在 >>> <<< 标记的地方检测到语法错误。“Bailing out”意味着没有试图恢复。有时你会得到更多的帮助-关于错误是什么,比如大括号或括弧不匹配。 因为存在句法错误,awk就不会尝试执行这个程序。然而,有些错误,直到你的程序被执行才会检测出来。例如,如果你试图用零去除某个数,awk会在这个除法的地方停止处理并报告输入行的行号以及在程序中的行号(这话是什么意思?难道输入行的行号是忽略空行后的行号?)。
1.2 简单输出 我们通常只会列出程序部分, 而不是整个命令行. 在任何情况下, 程序都可以用 引号包含起来放到 awk 命令的地一个参数中运行, 就像上文中展示的那样, 或者 把它放到一个文件中使用 awk 的 -f 参数调用它. 在 awk 中仅仅只有两种数据类型: 数值 和 字符构成的字符串. emp.data 是 一个包含这类信息的典型文件 – 混合了被空格和(或)制表符分割的数字和词语. Awk 程序一次从输入文件的中读取一行内容并把它分割成一个个字段, 通常默认 情况下, 一个字段是一个不包含任何空格或制表符的连续字符序列. 当前输入的 行中的地一个字段被称做 $1, 第二个是 $2, 以此类推. 整个行的内容被定 义为 $0. 每一行的字段数量可以不同. 通常, 我们要做的仅仅只是打印出每一行中的某些字段, 也许还要做一些计算. 这一节的程序基本上都是这种形式. 打印每一行
如果一个动作没有任何模式, 这个动作会对所有输入的行进行操作. print 语 句用来打印(输出)当前输入的行, 所以程序
打印特定字段
{ print $1, $3 } NF, 字段数量 很显然你可能会发现你总是需要通过 $1, $2 这样来指定不同的字段, 但任何表 达式都可以使用在$之后来表达一个字段的序号; 表达式会被求值并用于表示字段 序号. Awk会对当前输入的行有多少个字段进行计数, 并且将当前行的字段数量存 储在一个内建的称作 NF 的变量中. 因此, 下面的程序
{ print NF, $1, $NF } 计算和打印
你也可以对字段的值进行计算后再打印出来. 下面的程序 打印行号
Awk提供了另一个内建变量, 叫做 NR, 它会存储当前已经读取了多少行的计数. 我们可以使用 NR 和 $0 给 emp.data 的没一行加上行号:
1 Beth 4.00 0
你当然也可以在字段中间或者计算的值中间打印输出想要的内容:
total pay for Beth is 0
1.3 高级输出 字段排队
printf 语句的形式如下:: 这里有个程序使用 printf 打印每位员工的总薪酬::
{ printf("total pay for %s is $%.2f\n", $1, $2 * $3) }
total pay for Beth is $0.00
另一个程序是打印每位员工的姓名与薪酬:: 排序输出
假设你想打印每位员工的所有数据,包括他或她的薪酬,并以薪酬递增的方式进行排序输出。最简单的方式是使用awk将每位员工的总薪酬置于其记录之前,然后利用一个排序程序来处理awk的输出。Unix上,命令行如下: Awk的模式适合用于为进一步的处理从输入中选择相关的数据行。由于不带动作的模式会打印所有匹配模式的行,所以很多awk程序仅包含一个模式。本节将给出一些有用的模式示例。 通过对比选择
这个程序使用一个对比模式来选择每小时赚5美元或更多的员工记录,也就是,第二个字段大于等于5的行::
程序 通过文本内容选择
除了数值测试,你还可以选择包含特定单词或短语的输入行。这个程序会打印所有第一个字段为 Susie 的行:: 模式组合
可以使用括号和逻辑操作符与 && , 或 || , 以及非 ! 对模式进行组合。程序: 数据验证 实际的数据中总是会存在错误的。在数据验证-检查数据的值是否合理以及格式是否正确-方面,Awk是个优秀的工具。
数据验证本质上是否定的:不是打印具备期望属性的行,而是打印可疑的行。如下程序使用对比模式 将5个数据合理性测试应用于 emp.data 的每一行::
NF != 3 { print $0, "number of fields is not equal to 3" }
$2 < 3.35 { print $0, "rate is below minimum wage" } $2 > 10 { print $0, "rate exceeds $10 per hour" } $3 < 0 { print $0, "negative hours worked" } $3 > 60 { print $0, "too many hours worked" } 如果没有错误,则没有输出。 BEGIN与END
特殊模式 BEGIN 用于匹配第一个输入文件的第一行之前的位置, END 则用于匹配处理过的最后一个文件的最后一行之后的位置。这个程序使用 BEGIN 来输出一个标题::
BEGIN { print "Name RATE HOURS"; print ""}
Beth 4.00 0 程序的动作部分你可以在一行上放多个语句,不过要使用分号进行分隔。注意 普通的 print 是打印当前输入行,与之不同的是 print “” 会打印一个空行。 1.5 使用AWK进行计算 一个动作就是一个以新行或者分号分隔的语句序列。你已经见过一些其动作仅是单个 print 语句的例子。本节将提供一些执行简单的数值以及字符串计算的语句示例。在这些语句中,你不仅可以使用像 NF 这样的内置变量,还可以创建自己的变量用于计算、存储数据诸如此类的操作。awk中,用户创建的变量不需要声明。 计数
这个程序使用一个变量 emp 来统计工作超过15个小时的员工的数目::
$3 > 15 { emp = emp + 1 }
END { print emp, "employees worked more than 15 hours" }
对于第三个字段超过15的每行, emp 的前一个值加1。以 emp.data 为输入,该程序产生:: 求和与平均值
为计算员工的数目,我们可以使用内置变量 NR ,它保存着到目前位置读取的行数;在所有输入的结尾它的值就是所读的所有行数。
END { print NR, "employees" }
输出为:: 6 employees 如下是一个使用 NR 来计算薪酬均值的程序:: { pay = pay + $2 * $3 } END { print NR, "employees" print "total pay is", pay print "average pay is", pay/NR }
第一个动作累计所有员工的总薪酬。 END 动作打印出 处理文本
awk的优势之一是能像大多数语言处理数字一样方便地处理字符串。awk变量可以保存数字也可以保存字符串。这个程序会找出时薪最高的员工::
$2 > maxrate { maxrate = $2; maxemp = $1 }
END { print "highest hourly rate:", maxrate, "for", maxemp } 输出 highest hourly rate: 5.50 for Mary 这个程序中,变量 maxrate 保存着一个数值,而变量 maxemp 则是保存着一个字符串。(如果有几个员工都有着相同的最大时薪,该程序则只找出第一个。) 字符串连接
可以合并老字符串来创建新字符串。这种操作称为 连接(concatenation) 。程序
{ names = names $1 " "}
END { print names } 通过将每个姓名和一个空格附加到变量 names 的前一个值, 来将所有员工的姓名收集进单个字符串中。最后 END 动作打印出 names 的值::
Beth Dan Kathy Mark Mary Susie 打印最后一个输入行
虽然在 END 动作中 NR 还保留着它的值,但 $0 没有。程序 内置函数
我们已看到awk提供了内置变量来保存某些频繁使用的数量,比如:字段的数量和输入行的数量。类似地,也有内置函数用来计算其他有用的数值。除了平方根、对数、随机数诸如此类的算术函数,也有操作文本的函数。其中之一是 length ,计算一个字符串中的字符数量。例如,这个程序会计算每个人的姓名的长度::
{ print $1, length($1) }
Beth 4 行、单词以及字符的计数
这个程序使用了 length 、 NF 、以及 NR 来统计输入中行、单词以及字符的数量。为了简便,我们将每个字段看作一个单词。
{ nc = nc + length($0) + 1
nw = nw + NF } END { print NR, "lines,", nw, "words,", nc, "characters" } 文件 emp.data 有:
6 lines, 18 words, 77 characters 1.6 控制语句 Awk为选择提供了一个 if-else 语句,以及为循环提供了几个语句,所以都效仿C语言中对应的控制语句。它们仅可以在动作中使用。 if-else语句
如下程序将计算时薪超过6美元的员工的总薪酬与平均薪酬。它使用一个 if 来防范计算平均薪酬时的零除问题。
$2 > 6 { n = n + 1; pay = pay + $2 * $3 }
END { if (n > 0) print n, "employees, total pay is", pay, "average pay is", pay/n else print "no employees are paid more than $6/hour" }
emp.data 的输出是: while语句 一个 while 语句有一个条件和一个执行体。条件为真时执行体中的语句会被重复执行。这个程序使用公式 value=amount(1+rate)years
来演示以特定的利率投资一定量的钱,其数值是如何随着年数增长的。
# interest1 - 计算复利
{ i = 1 条件是 while 后括弧包围的表达式;循环体是条件后大括号包围的两个表达式。 printf 规格字符串中的 \t 代表制表符; ^ 是指数操作符。从 # 开始到行尾的文本是注释,会被awk忽略,但能帮助程序的读者理解程序做的事情。
你可以为这程序输入三个一组的数字,看看不一样的钱数、利率、以及年数会产生什么。例如,如下事务演示了1000美元,利率为6%与12%,5年的复利分别是如何增长的::
$ awk -f interest1
1000 .06 5 1060.00 1123.60 1191.02 1262.48 1338.23 1000 .12 5 1120.00 1254.40 1404.93 1573.52 1762.34 for语句
另一个语句, for ,将大多数循环都包含的初始化、测试、以及自增压缩成一行。如下是之前利息计算的 for 版本::
# interest1 - 计算复利
{ for (i = 1; i <= $3; i = i + 1) 初始化 i = 1 只执行一次。接下来,测试条件 i <= $3 ;如果为真,则执行循环体的 printf 语句。循环体执行结束后执行自增 i = i + 1 ,接着由另一次条件测试开始下一个循环迭代。代码更加紧凑,并且由于循环体仅是一条语句,所以不需要大括号来包围它。 1.7 数组
awk为存储一组相关的值提供了数组。虽然数组给予了awk很强的能力,但在这里我们仅展示一个简单的例子。如下程序将按行逆序打印输入。第一个动作将输入行存为数组 line 的连续元素;即第一行放在 line[1] ,第二行放在 line[2] , 依次继续。 END 动作使用一个 while 语句从后往前打印数组中的输入行:: # 反转 - 按行逆序打印输入 { line[NR] = $0 } # 记下每个输入行
END { i = NR # 逆序打印
如下是使用 for 语句实现的相同示例: # 反转 - 按行逆序打印输入 { line[NR] = $0 } # 记下每个输入行
END { for (i = NR; i > 0; i = i - 1)
以上详细介绍了awk命令的用法,awk语句结构等内容,希望有助于大家掌握awk命令的全用方法。 |