Linux三剑客之awk
awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。它支持用户自定义函数和动态正则表达式等先进功能,它在命令行中使用,但更多是作为脚本来使用。awk有很多内建的功能,比如数组、函数等
1、基本用法
1)直接使用awk命令调用,选项F用于指定域分隔符,默认为空格,格式如下:
awk [-F 分隔符] 'PATTERN {ACTION}' FILENAME...
2)先将要输入的选项、模式和动作放入一个脚本文件中,然后使用选项f调用:格式如下
awk -f SCRIPT FILENAME...
2、PATTERN
1)支持正则表达式
# 输出/etc/passwd中以b或r开头的行
[root@acer ~]# awk '/^[b,r]/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
2)支持表达式
# 输出用户uid小于10的用户名及其uid
[root@acer ~]# awk -F: '$3<10{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
3)支持模式匹配范围(最小匹配)从第一个pattern1开始到第一个pattern2结束
# 输出root到halt之间的用户
[root@acer ~]# awk -F: '/^root/,/^halt/{print $1}' /etc/passwd
root
bin
daemon
adm
lp
sync
shutdown
halt
4)BEGIN/END在awk命令执行前/后执行一次
# 为2)中的输出加上表头“name、uid”,表尾“Screening is complete”
[root@acer ~]# awk -F: 'BEGIN{print "name uid"}''$3<10{print $1,$3}END{print "Screening is complete"}' /etc/passwd
name uid
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
Screening is complete
5)空模式:即对所有行执行ACTION操作
# 输出所有用户及其uid
[root@acer ~]# awk -F: '{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
uucp 10
operator 11
games 12
gopher 13
ftp 14
nobody 99
dbus 81
vcsa 69
abrt 173
haldaemon 68
ntp 38
saslauth 499
postfix 89
sshd 74
tcpdump 72
tom 500
named 25
dhcpd 177
apache 48
mysql 27
新建一个文本文档person.txt,内容如下:
1,zhangsan,teacher
2,lisi,CEO
3,wangwu,student
4,zhaoliu,CEO
5,huangquan,student
3、ACTION表示对匹配到行的切片进行处理,常用的有
print:输出,默认输出分割符为空格
printf:格式化输出,用法与C语言中printf一样,格式一般为printf FORMAT,[ARGUMENT...],但是printf默认输出后不换行,所以一般要与“\n”转义字符连用
常用FORMAT及其说明
%c:作为一个ASCII码字符输出
%s:字符串
%o:八进制
%x:十六进制
%d:整数
%e:科学型输出
%f:浮点型输出
%%:显示%
还可以使用修饰符来修饰输出格式
N:显示宽度
-:表示左对齐(默认右对齐)
# 格式化输出person.txt中的内容
[root@acer ~]# awk -F, '{printf "%-3s%-10s%-10s\n",$1,$2,$3}' person.txt
1 zhangsan teacher
2 lisi CEO
3 wangwu student
4 zhaoliu CEO
5 huangquan student
4、内置变量
1)记录变量
FS:指定输入分隔符,相当于使用-F选项
OFS:指定输出分隔符
RS:输入文本信息所使用的换行符,一般默认为空格即可
ORS:输出文本信息所使用的换行符,一般默认为空格即可
[root@acer ~]# awk 'BEGIN{FS=",";OFS=":"}{print $1,$2,$3}' person.txt
1:zhangsan:teacher
2:lisi:CEO
3:wangwu:student
4:zhaoliu:CEO
5:huangquan:student
2)数据变量
NR:awk命令处理的总行数
NF:当前处理行分割后的字段总数
FNR:awk命令处理的当前文件的行数
# NR可以输出awk需要处理的总行数
[root@acer ~]# awk -F, '{print NR}' person.txt
1
2
3
4
5
# NF可以输出当前处理行分割后的字段总数,可以与$连用,表示输出最后一个字段
[root@acer ~]# awk -F, '{print NF}' person.txt
3
3
3
3
3
[root@acer ~]# awk -F, '{print $NF}' person.txt
teacher
CEO
student
CEO
student
# NFR表示有多个文件时awk命令处理当前文件的行数
# 如:awk.txt文件中有如下内容
[root@acer ~]# cat awk.txt
this,is,a,test.
# 则有
[root@acer ~]# awk '{print FNR}' person.txt awk.txt
1
2
3
4
5
1
3)用户自定义变量
# 在BEGIN中定义变量
[root@acer ~]# awk 'BEGIN{test="hello awk";print test}'
hello awk
# 使用-v选项定义变量(但是这种用法好像必须要加BEGIN,所以个人感觉上一种会好一点)
[root@acer ~]# awk -v test="hello awk" 'BEGIN{print test}'
hello awk
5、高级用法
1)awk支持操作符
算术操作符、字符操作符、赋值操作符、比较操作符、逻辑关系符、条件表达式及函数调用等
2)awk支持控制语句(与Java控制语句语法基本相同)
if-else
if(condition){statement;...}else {statement;...}
while
while(condition){statement;...}
do-while
do{statement;...}while(condition)
for
for(初始化;布尔表达式;更新) {statement;...}
for(声明语句:表达式){statement;...}
case
switch(expression){case VALUE or /REGEXP/:statement;...default:statement;...}
break:提前结束循环
contuinue:进入下一轮循环
next:提前进入下一行文本处理
3)awk支持数组
可以自定义下标,如a[a]=a;a[b]=b;...
如果没有定义下标,下标默认从1开始
如果要遍历数组中每个元素,则要使用for循环结构来遍历
for(声明语句:表达式){statement;...}
# 统计并输出/etc/passwd中不同bash的用户个数
[root@acer ~]# awk -F: '{shell[$NF]++}END{for(A in shell){print A,shell[A]}}' /etc/passwd
/sbin/shutdown 1
/bin/csh 1
/bin/bash 2
/sbin/nologin 21
/sbin/halt 1
/bin/sync 1
# 上述命令中,shell是一个数组,而shell[$NF]分别可以表示为shell数组中以不同shell为下标的数组元素,统计之并用for循环结构依次输出
# 统计并输出person.txt中最后一个字段不同的用户个数
[root@acer ~]# awk -F, '{job[$NF]++}END{for(I in job){print I,job[I]} }' person.txt
student 2
teacher 1
CEO 2
# 统计并输出不同IP地址对当前web服务访问的次数
[root@acer ~]# awk '{ip[$1]++}END{for(I in ip){print I,ip[I]}}' /var/log/httpd/access_log