一、awk介绍
awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”
它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。
awk 是一种很棒的语言,它适合文本处理和报表生成,其语法较为常见,借鉴了某些语言的一些精华,如 C 语言等。
awk程序由一个主输入循环(Main input loop)维持,主输入循环反复执行,直到条件被触发,主输入循环无须由程序员去写,awk已经搭好主输入循环的框架
二、awk模式匹配
任何awk语句都由模式(pattern
)和动作(action
)组成
- 模式是由一组用于测试输入行是否需要执行动作的规则
- 动作是包含语句,函数和表达式的执行过程
简言之,模式决定动作何时触发和触发事件,动作执行对输入行的处理
1. awk的第一种调用方式
[root@server1 ~]# touch input
[root@server1 ~]# awk '/^$/{print "This is a blank line."}' input
[root@server1 ~]# vim input
[root@server1 ~]# awk '/^$/{print "This is a blank line."}' input
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
[root@server1 ~]# cat input
其中:
单引号中间是awk命令,该awk命令由两部分组成,以/
符号分隔,^$
部分是模式,花括号部分是动作,该awk命令表示一旦读入的输入文件行是空行,就打印“This is a blank line”,^$
是正则表达式,表示空白行,print
表示该动作是打印操作,input
是输入文件名称
2:awk的第二种调用方式
使用awk -f
调用scr.awk
文件
[root@server1 ~]# vim scr.awk
[root@server1 ~]# awk -f scr.awk input
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
[root@server1 ~]# cat scr.awk
/^$/{print "This is a blank line."}
3. 直接使用awk脚本文件的方法来调用
这种方式需要在脚本的开头加#!/bin/awk -f
[root@server1 ~]# vim scr.awk
[root@server1 ~]# chmod o+x scr.awk
[root@server1 ~]# ./scr.awk input
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
[root@server1 ~]# cat scr.awk
#!/bin/awk -f
#第一行表示用什么来执行脚本
/^$/{print "This is a blank line."}
三、记录和域
awk认为输入文件是结构化的,awk将每个输入文件行定义为记录,行中的每个字符串定义为域,域之间用空格,Tab键或其他符号进行分隔,分隔域的符号就叫做分隔符
awk定义域操作符$
来指定执行动作的域,域操作符$
后面跟数字或变量来标识域的位置,每条记录的域从1开始编号,如$1
表示第一个域 $0
表示所有域
1. 打印指定的域
[root@server1 ~]# awk '{print $2,$1,$4,$3}' stucoed
Li Tom xian 028-12345678
Li Tom xian 028-12345678
Li Tom xian 028-12345678
Li Tom xian 028-12345678
Li Tom xian 028-12345678
Li Tom xian 028-12345678
Li Tom xian 028-12345678
Li Tom xian 028-12345678
[root@server1 ~]# cat stucoed
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
2. 打印全部的域
[root@server1 ~]# awk '{print $0}' stucoed Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
3.域操作符$后面还可以跟变量,或者变量运算表达式
[root@server1 ~]# awk 'BEGIN {one=1;two=2} {print $(one+two)}' stucoed
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
4.awk默认的设置是空格键,Tab键被看作连续的空格键来处理
[root@server1 ~]# cat stucoed
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
[root@server1 ~]# awk 'BEGIN {one=1;two=2} {print $(one+two)}' stucoed
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
5.可以使用-F来改变分隔符
注意看下面的例子 电话和xian之间用空格分开的化被看作是一个域
其中:\t
表示tab,Tom和Li用tab分割,Li与电话之间用tab分隔
[root@server1 ~]# cat stucoed
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
Tom Li 028-12345678 xian
[root@server1 ~]# awk -F "\t" '{print $3}' stucoed
028-12345678 xian
028-12345678 xian
028-12345678 xian
028-12345678 xian
028-12345678 xian
028-12345678 xian
028-12345678 xian
028-12345678 xian
6.尽管-F选项可以改变分隔符 但是 awk还提供了另一种更方便,实用的方法来改变分隔符,这就是使用awk环境变量FS
我们可以通过在BEGIN字段中设置FS的值来改变分隔符
[root@server1 ~]# awk 'BEGIN {FS=","} {print $0}' stucoed
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian
7. 打印第1域和第3域
[root@server1 ~]# awk 'BEGIN {FS=","} {print $1,$3}' stucoed
Tom 028-12345678 xian
Tom 028-12345678 xian
Tom 028-12345678 xian
Tom 028-12345678 xian
Tom 028-12345678 xian
Tom 028-12345678 xian
Tom 028-12345678 xian
Tom 028-12345678 xian
对于不同的输入文件 需要根据实际情况设置相应的分隔符 如linux的/etc/passwd
文件是以冒号为分隔符的
四、关系和布尔运算符
awk定义了一组关系运算符用于awk模式匹配
1.使用匹配正则表达式~
符号
第一个域匹配root
[root@server1 ~]# awk 'BEGIN {FS=":"} $1~/root/' /tmp/passwd
root:x:0:0:root:/root:/bin/bash
全部域匹配root
[root@server1 ~]# awk 'BEGIN {FS=":"} $0~/root/' /tmp/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
全部域不匹配nologin
[root@server1 ~]# awk 'BEGIN {FS=":"} $0!~/nologin/' /tmp/passwd
root:x:0:0:root:/root:/bin/bash
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
dd:x:1000:1000::/home/dd:/bin/bash
2.awk在进行模式匹配时,常常使用到条件语句
awk条件语句与C语言类似
注意:if后面的条件需要用圆括号括起来
[root@server1 ~]# awk 'BEGIN {FS=":"} {if ($3<$4) print $0}' /tmp/passwd
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
多条件精确匹配
在文件中查找满足$3==1
或者$4==10
的记录
利用==
符号进行的匹配可称为精确匹配
[root@server1 ~]# awk 'BEGIN {FS=":"} {if ($3==1||$4==10) print $0}' /tmp/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
多条件模糊匹配
将==
改为~
符号 表示模糊匹配 所查询内容包含1或者10这个字符就可以
[root@server1 ~]# awk 'BEGIN {FS=":"} {if ($3~1||$4~10) print $0}' /tmp/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
dd:x:1000:1000::/home/dd:/bin/bash
五、表达式
与其他编程语言一样,awk表达式用于存储,操作和获取数据
一个awk表达式可由数值,字符常量,变量,操作符 函数和正则表达式自由组合而成变量是一个值的标识符,定义awk变量非常方便,只需定义一个变量名并将值赋给它即可
其中:
- 变量名只能包含字母,数字和下划线 而且不能以数字开头
- 定义awk变量无须声明变量类型,每个变量有两种类型的值:字符串值和数值
- awk根据表达式上下文来确定使用哪个值
- 变量的默认数值为0,默认字符串值为空
1.统计input文件中的空白行
[root@server1 ~]# awk '/^$/{print x+=1}' input
1
2
3
4
5
6
7
8
9
++x /x++ --x/x-- 的区别
x++:即返回x值后,x变量增加1
++x:即x变量增加1,再返回x值
[root@server1 ~]# awk '/^$/{print x++}' input
0
1
2
3
4
5
6
7
8
[root@server1 ~]# awk '/^$/{print ++x}' input
1
2
3
4
5
6
7
8
9
2.计算平均值的例子
[root@server1 ~]# cat stucoed
Tom,Li,028-1234567,85,92,97,88
Tom,Li,028-1234567,85,92,97,88
Tom,Li,028-1234567,85,92,97,88
Tom,Li,028-1234567,85,92,97,88
Tom,Li,028-1234567,85,92,97,88
Tom,Li,028-1234567,85,92,97,88
[root@server1 ~]# vim scr2.awk
[root@server1 ~]# chmod +x scr2.awk #给脚本执行权限
[root@server1 ~]# ./scr2.awk stucoed #输出学生姓名和平均成绩
Tom 90.5
Tom 90.5
Tom 90.5
Tom 90.5
Tom 90.5
Tom 90.5
[root@server1 ~]# cat scr2.awk
#!/bin/awk -f
BEGIN {FS=","}
{total=$4+$5+$6+$7
avg=total/4
print $1,avg}
六、系统变量
awk定义了很多内建变量用于设置环境信息,我们称它为系统变量
这些系统变量可分为:
- 第一种用于改变awk的默认值,如域分隔符
- 第二种用于定义系统值,在处理文本时可以读取这些系统值 如记录中的域数量,当前记录数,当前文件名等
符号 | 含义 |
---|---|
$n | 当前记录的第n个域 域间由FS分隔 |
$0 | 记录所有的域 |
FS | 字段分隔符,默认是空格键 |
NF | 当前记录中的域数量 |
NR | 当前记录数(被awk处理过的行数) |
FILENAME | 保存了当前的输入文件名 |
[root@server1 ~]# awk 'BEGIN {FS=","} {print NF,NR,$0} END {print FILENAME}' stucoed
7 1 Tom,Li,028-1234567,85,92,97,88
7 2 Tom,Li,028-1234567,85,92,97,88
7 3 Tom,Li,028-1234567,85,92,97,88
7 4 Tom,Li,028-1234567,85,92,97,88
7 5 Tom,Li,028-1234567,85,92,97,88
7 6 Tom,Li,028-1234567,85,92,97,88
7 7 Tom,Li,028-1234567,85,92,97,88
7 8 Tom,Li,028-1234567,85,92,97,88
7 9 Tom,Li,028-1234567,85,92,97,88
7 10 Tom,Li,028-1234567,85,92,97,88
7 11 Tom,Li,028-1234567,85,92,97,88
7 12 Tom,Li,028-1234567,85,92,97,88
7 13 Tom,Li,028-1234567,85,92,97,88
7 14 Tom,Li,028-1234567,85,92,97,88
stucoed
七、格式化输出
前面的例子只涉及awk如何输入文件进行处理,对于输出的格式并未规定。而awk的一大主要功能是产生报表,报表就是要求按照于定的格式输出,awk借鉴c语言的语法,定义了printf
输出语句,它可以规定输出的格式
示例1:
实现要求:
从域号获取值 $2号域与%s对应 为字符串,$8号域与%d对应 为整数值,两个域之间用Tab键隔开(\t:Tab键),每输出两个域换行(\n表示换行)
[root@server1 ~]# awk 'BEGIN {FS=","} {printf("%s\t%d\n",$2,$8)}' stucoed
Li 0
Li 0
Li 0
Li 0
Li 0
Li 0
Li 0
Li 0
Li 0
Li 0
Li 0
Li 0
Li 0
Li 0
2.ASCII字符的转换
[root@server1 ~]# awk 'BEGIN {printf("%c\n",65)}'
A
3.浮数的转换
[root@server1 ~]# awk 'BEGIN {printf("%f\n",2010)}'
2010.000000
4.printf修饰符的例子
以字符串格式输出文件的第1和第3号域,并对第1个%s进行了修饰
-15表示该字符串长度控制为15位,并且左对齐,若字符串不足15位 则用空格补齐
[root@server1 ~]# awk 'BEGIN {FS=","} {printf("%-15s\t%s\n",$1,$3)}' stucoed
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
[root@server1 ~]# awk 'BEGIN {FS=","} {printf("%-15s\t%s\n",$1,$3)}' stucoed
Tommmm 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
5.如果我们要在输出的域名上加解释语言 可以在BEGIN字段中添加相应的输出注释
[root@server1 ~]# awk 'BEGIN {FS=",";print "Name\t\tPhonenumber"} {printf("%-15s\t%s\n",$1,$3)}' stucoed
Name Phonenumber
Tommmm 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
Tom 028-1234567
6.小数点后保留3位
[root@server1 ~]# awk 'BEGIN {printf("%.3f\n",2020.1212)}'
2020.121
八、内置字符串函数
awk提供了强大的内置字符串函数 用于实现文本的字符串替换 查找以及分割等功能
8.1 gsub函数
gsub函数执行字符串替换功能,它将第一个字符串替换为第二个字符串,gsub函数有两种形式,第一种形式作用于全部域 即$0
第二种形式作用于域t
OFS:输出域分隔符 默认是空格
1.替换第一域上的root字符串
[root@server1 ~]# awk 'BEGIN {FS=":";OFS=":"} gsub(/root/,"dd", $1) {print $0}' /tmp/passwd
dd:x:0:0:root:/root:/bin/bash
2.替换全部域上的root字符串
[root@server1 ~]# awk 'BEGIN {FS=":";OFS=":"} gsub(/root/,"dd") {print $0}' /tmp/passwd
dd:x:0:0:dd:/dd:/bin/bash
operator:x:11:0:operator:/dd:/sbin/nologin
8.2 index和length函数的用法
1. index返回第二个字符串在第一个字符串出现的首位置
[root@server1 ~]# awk 'BEGIN {print index("gridsphere","ph")}'
6
2. length返回字符串的长度
[root@server1 ~]# awk 'BEGIN {print length("gridsphere")}'
10
8.3match(s,t)测试s是否包含t的字符串
t
可以是一个正则表达式 若匹配成功 返回匹配t
的首位置
若匹配不成功 则返回0
1.在gridsphere字符串中匹配D/d
awk默认状态是区分大小写的
[root@server1 ~]# awk 'BEGIN {print match("gridsphere",/D/)}'
0 #匹配不成功返回0
[root@server1 ~]# awk 'BEGIN {print match("gridsphere",/d/)}'
4
2.先定义str变量,赋值,然后用sub函数讲pro改成大写的PRO str有两个pro
结果显示只有一个pro改成了大写的PRO
[root@server1 ~]# awk 'BEGIN {str="multiprocessor programming";sub(/pro/,"PRO",str);printf("%s\t",str)}'
multiPROcessor programming