AWK程序语言设计-入门指南

一、AWK入门指南

 1.1 起步

  本文所有示例都在Ubuntu20.4+GNU Awk 5.0.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.2.0)上完成。可使用如下指令安装, 一般安装系统时都会自带awk.

apt-get install gawk

  在安装awk时一开始使用“apt-get install awk”安装, 结果安装失败,提示选择以下安装包安装。具体的区别可以参考这篇文章

 original-awk 2012-12-20-6
 mawk:i386 1.3.4.20200120-2
 gawk:i386 1:5.0.1+dfsg-1
 mawk 1.3.4.20200120-2
 gawk 1:5.0.1+dfsg-1

创建一个测试用的文档awk-sample, 内容如下:

NAME	GENDER	HEIGHT(cm)	MONTH	SALARY
zhao	male	150			1		1000
qian	female	165			0		500
sun		female	177			2		5000
li		male	180			0		7000
zhou	male	155			5		3000
wu		male	170			12		1500
zheng	female	167			4		3400
wang	male	158			3		8000

打印身高大于1.6米以上的人的总薪资:

$ awk '$3>160 {print $1, $4 * $5}' awk-sample
NAME 0
qian 0
sun 10000
li 0
wu 18000
zheng 13600

从这里可以看出以下几点:

  • awk单引号中的结构: 模式+执行操作,模式用于匹配过滤行,操作用于输出结果
  • awk默认以按空格来分割,从左到右排序且从1开始,使用$来引用
  • 文档中出现的数字可以直接参与运算。区别于C,在C中需要先做类型转换
  • 模式中类似C语言中的运算,若要使用字符运算,记得加上双引号
  • 这里的逗号输出是相当于空格

为了验证最后一点,尝试做以下操作:

$ awk '$1 == "NAME" {print $1}' awk-sample 
NAME

在测试中发现:

  • 在字符串的比较中,按照字母ascii值从左到右比较, 这点与C中strcmp()相似
  • 在字符串和数值的比较中,似乎字符永远比数值大,测试如下:
$ awk '$1>0 {print $1}' awk-sample 
NAME
zhao
qian
sun
li
zhou
wu
zheng
wang
$ awk '$1>65535 {print $1}' awk-sample 
NAME
zhao
qian
sun
li
zhou
wu
zheng
wang
$ awk '$1>9999999 {print $1}' awk-sample 
NAME
zhao
qian
sun
li
zhou
wu
zheng
wang
$ awk '$1<9999999 {print $1}' awk-sample
$ awk '$1<=9999999 {print $1}' awk-sample
$ awk '$1==9999999 {print $1}' awk-sample

AWK的结构:awk + 程序 + 文件

awk 'program' file1 file2 ...

AWK的程序:(program):pattern { action }
  和sed类似,awk的处理方式是按行处理,从第一行到最后一行, 依次匹配模式,然后执行动作。模式和动作不能同时省略。
  当程序比较复杂的时候, 可以使用选项-f指定程序文件。
程序文件awk-program

# 程序文件awk-program,注意不再需要单引号
$1>"NAME" {print $1, $3}

运行:

$ awk -f awk-program awk-sample 
zhao 150
qian 165
sun 177
li 180
zhou 155
wu 170
zheng 167
wang 158

AWK的文件: file1 file2 …
  可以同时处理多个文件,文件也可以指stdin。当不输入文件名称时,终端中接着输入的任意数据行,直到输入一个文件结束信号(Ctrl+C,Ctrl+D)

$ awk '{print $1}'
123 456 789
123
000 111 222
000
Ctrl+C

 1.2 简单输出

  参考原文中的介绍
  在 awk 中仅仅只有两种数据类型: 数值字符构成的字符串。 awk-sample是 一个包含这类信息的典型文件 – 混合了被空格和(或)制表符分割的数字和词语。
  Awk 程序一次从输入文件的中读取一行内容并把它分割成一个个字段, 通常默认情况下, 一个字段是一个不包含任何空格或制表符的连续字符序列。 当前输入的行中的地一个字段被称做 $1, 第二个是 $2, 以此类推. 整个行的内容被定 义为 $0.。每一行的字段数量可以不同。

  打印行
  awk程序中可以没有模式,这时候print打印每一行, $0代表整行, 所以下面两句是同样上的效果

$ awk '{print}' awk-sample 
NAME	GENDER	HEIGHT(cm)	MONTH	SALARY
zhao	male	150		1	1000
qian	female	165		0	500
sun	female	177		2	5000
li	male	180		0	7000
zhou	male	155		5	3000
wu	male	170		12	1500
zheng	female	167		4	3400
wang	male	158		3	8000
$ awk '{print $0}' awk-sample 
NAME	GENDER	HEIGHT(cm)	MONTH	SALARY
zhao	male	150		1	1000
qian	female	165		0	500
sun	female	177		2	5000
li	male	180		0	7000
zhou	male	155		5	3000
wu	male	170		12	1500
zheng	female	167		4	3400
wang	male	158		3	8000

  打印特定段
  $后跟指定的数字, 超出的字段不会打印,如$6

$ awk '{print $1, $3, $6}' awk-sample 
NAME HEIGHT(cm)
zhao 150
qian 165
sun 177
li 180
zhou 155
wu 170
zheng 167
wang 158

  NF, 字段数量
  awk用很多内置变量,NF变量会存储当前输入的行的字段数量,所以$NF就表示最后一个字段,效果如下:

$ awk '{ print NF, $1, $NF }' awk-sample 
5 NAME SALARY
5 zhao 1000
5 qian 500
5 sun 5000
5 li 7000
5 zhou 3000
5 wu 1500
5 zheng 3400
5 wang 8000

  NR, 行号

$ awk '{print NR, $0}' awk-sample 
1 NAME	GENDER	HEIGHT(cm)	MONTH	SALARY
2 zhao	male	150		1	1000
3 qian	female	165		0	500
4 sun	female	177		2	5000
5 li	male	180		0	7000
6 zhou	male	155		5	3000
7 wu	male	170		12	1500
8 zheng	female	167		4	3400
9 wang	male	158		3	8000

以下是awk的内建变量表:

变量名称 含义
$0 当前行
$1 ~ $n 当前记录的第n个字段,字段间由FS分隔
FS 输入字段分隔符 默认是空格
NF 当前记录中的字段个数,就是有多少列
NR 已经读出的记录数,就是行号,从1开始
RS 输入的记录他隔符默 认为换行符
OFS 输出字段分隔符 默认也是空格
ORS 输出的记录分隔符,默认为换行符
ARGC 命令行参数个数
ARGV 命令行参数数组
FILENAME 当前输入文件的名字
IGNORECASE 如果为真,则进行忽略大小写的匹配
ARGIND 当前被处理文件的ARGV标志符
CONVFMT 数字转换格式 %.6g
ENVIRON UNIX环境变量
ERRNO UNIX系统错误消息
FIELDWIDTHS 输入字段宽度的空白分隔字符串
FNR 当前记录数
OFMT 数字的输出格式 %.6g
RSTART 被匹配函数匹配的字符串首
RLENGTH 被匹配函数匹配的字符串长度
SUBSEP \034

 1.3 高级输出

  格式化输出
类似C语言中的代码格式,print, printf的用法都很相似
格式: printf(format, value1, value2, …, valuen)
比如:

$ awk '{ printf("total pay for %s is $%.2f\n", $1, $4 * $5) }' awk-sample 
total pay for NAME is $0.00
total pay for zhao is $1000.00
total pay for qian is $0.00
total pay for sun is $10000.00
total pay for li is $0.00
total pay for zhou is $15000.00
total pay for wu is $18000.00
total pay for zheng is $13600.00
total pay for wang is $24000.00

 1.4 选择

awk的程序结构:模式 {动作}
在模式中可以使用 >, <, ==,&&,||,! 等这些运算符

$ awk '($1=="wu"||$1=="zhou")&&$5<=2000 { print $0}' awk-sample 
wu	male	170		12	1500

  BEGIN与END
  特殊模式 BEGIN 用于匹配第一个输入文件的第一行之前的位置, END 则用于匹配处理过的最后一个文件的最后一行之后的位置。这个程序使用 BEGIN 来输出一个
  注意这里print语句之间用了分号。

$ awk 'BEGIN { print "before 1    2    3    4   5"; print ""} {print} END{print "end 1    2    3    4   5"}' awk-sample 
before 1    2    3    4   5

NAME	GENDER	HEIGHT(cm)	MONTH	SALARY
zhao	male	150		1	1000
qian	female	165		0	500
sun	female	177		2	5000
li	male	180		0	7000
zhou	male	155		5	3000
wu	male	170		12	1500
zheng	female	167		4	3400
wang	male	158		3	8000
end 1    2    3    4   5

 1.5 使用AWK进行计算

  计数
用作数字的awk变量的默认初始值为0,不需要初始化
统计月薪资在1500以上的人的个数

$ awk '$5 >=1500 {print $0; sum = sum + 1} END {printf("SUM = %.2f\n", sum)}' awk-sample 
NAME	GENDER	HEIGHT(cm)	MONTH	SALARY
sun	female	177		2	5000
li	male	180		0	7000
zhou	male	155		5	3000
wu	male	170		12	1500
zheng	female	167		4	3400
wang	male	158		3	8000
SUM = 7.00

  计算平均薪资
利用NR计算平均薪资, NR代表当前处理的行号, 到最后输出的时候就代表了总的人数, 然后再通过变量统计总的资金, 最后就可以算出平均值。

awk '$5>0 {sum = sum + $5} END {print NR, "employees";print "total pay is", sum;print "average pay is", sum/NR}' awk-sample 
9 employees
total pay is 29400
average pay is 3266.67

  找出薪资最高的人

$ awk '$5>emp {emp=$5} END {print emp}' awk-sample 
SALARY

这里因为前面也说过,字符串总比数值大, 所以这里输出了SALARY, 可以用NR过滤掉

$ awk '$5>emp&&NR>1 {emp=$5} END {print emp}' awk-sample 
8000

  字符串连接

$ awk 'NR>1 {names = names $1 " "} END {print "names: " names}' awk-sample 
names: zhao qian sun li zhou wu zheng wang

注意只要不使用逗号分割
  打印最后一个输入行

$ awk '{last=$0} END {print last}' awk-sample 
wang	male	158		3	8000

  内置函数
除了内建变量, awk还提供了内建函数,比如用来计算字符串长度

$ awk '{print $0, length($0);sum = sum + length($0) + 1} END {print "sum: " sum}' awk-sample 
NAME	GENDER	HEIGHT(cm)	MONTH	SALARY 35
zhao	male	150		1	1000 21
qian	female	165		0	500 22
sun	female	177		2	5000 22
li	male	180		0	7000 19
zhou	male	155		5	3000 21
wu	male	170		12	1500 20
zheng	female	167		4	3400 24
wang	male	158		3	8000 21
sum: 214

  注意$0不包含换行符
awk内建算术函数:

函数名称 含义
atan2( y, x ) 返回 y/x 的反正切。
cos( x ) 返回 x 的余弦;x 是弧度。
sin( x ) 返回 x 的正弦;x 是弧度。
exp( x ) 返回 x 幂函数。
log( x ) 返回 x 的自然对数。
sqrt( x ) 返回 x 平方根。
int( x ) 返回 x 的截断至整数的值。
rand( ) 返回任意数字 n,其中 0 <= n < 1。
srand( [Expr] ) 将 rand 函数的种子值设置为 Expr 参数的值,或如果省略 Expr 参数则使用某天的时间。返回先前的种子值。

 1.6 控制语句

  awk提供for if-else while控制语句, 都是仿照C语言的风格。它们仅可以在动作中使用。
  if-else语句

$ awk '{if($5>1500) print $0}' awk-sample 
NAME	GENDER	HEIGHT(cm)	MONTH	SALARY
sun	female	177		2	5000
li	male	180		0	7000
zhou	male	155		5	3000
zheng	female	167		4	3400
wang	male	158		3	8000

  while语句和for语句

# interest1 - 计算复利
#   输入: 钱数    利率    年数
#   输出: 复利值

{   i = 1
    while (i <= $3) {
        printf("\t%.2f\n", $1 * (1 + $2) ^ i)
        i = i + 1
    }
}

换成for语句

# interest1 - 计算复利
#   输入: 钱数    利率    年数
#   输出: 每年末的复利

{ for (i = 1; i <= $3; i = i + 1)
    printf("\t%.2f\n", $1 * (1 + $2) ^ i)
}
$ awk -f interest1 
1000 .06 5
	1060.00
	1123.60
	1191.02
	1262.48
	1338.23
2000 .23 10
	2460.00
	3025.80
	3721.73
	4577.73
	5630.61
	6925.65
	8518.55
	10477.82
	12887.72
	15851.89

 1.7 数组

相比于C语言,减少了很多初始化的步骤, 有点像python的风格。

$ awk '{data[NR] = $0} END {print data[NR]}' awk-sample 
wang	male	158		3	8000

 1.8 正则匹配

程序结构: /REG/{action}
REG是正则表达是, awk会将符合正则表达式的行关联到action执行

$ awk '/NAME/{print $0}' awk-sample 
NAME	GENDER	HEIGHT(cm)	MONTH	SALARY
$ awk '/wu/{print $0}' awk-sample 
wu	male	170		12	1500
$ awk '/^[a-z]/{print $0}' awk-sample 
zhao	male	150		1	1000
qian	female	165		0	500
sun	female	177		2	5000
li	male	180		0	7000
zhou	male	155		5	3000
wu	male	170		12	1500
zheng	female	167		4	3400
wang	male	158		3	8000

关于正则表达式的详细说明, 可以参考:https://blog.csdn.net/hongrisl/article/details/83818199

猜你喜欢

转载自blog.csdn.net/weixin_43749427/article/details/113739728