Awk
是一种优良的文本处理工具,Linux 及 Unix 中现有的功能最强大的数据处理引擎之一。命令提供了极其强大的功能:可以进行正则表达式的匹配,样式装入、流控制、数学运算符、进程控制语句甚至于内置的变量和函数。它具备了一个完整的语言所应具有的几乎所有精美特性。
前言
历史
Awk
其名称得自于它的创始人阿尔佛雷德·艾侯(Alfred Aho)、彼得·温伯格(Peter Weinberger)和布莱恩·柯林汉(Brian Kernighan)姓氏的首个字母,Awk 被创建于 1978 年在诺基亚贝尔实验室。
实际上 Awk 不仅是一种程序,更是一种语言, Awk 是一种数据驱动型脚本语言,包含一系列针对文本数据流的操作,用于提取或转换文本,例如生成格式报表。该语言广泛使用字符串数据类型,关联数组(即由键字符串索引的数组)和正则表达式。 虽然 Awk 的应用领域有限,并且专门用于支持单线程序,但该语言是图灵完备的,甚至 Awk 的早期贝尔实验室用户也经常编写结构良好的大型程序。
gawk
是 Awk 的GNU
版本。
最简单地说,Awk
是一种用于处理文本的编程语言工具。Awk 在很多方面类似于 Shell 编程语言,尽管Awk
具有完全属于其本身的语法。它的设计思想来源于SNOBOL4
、sed
、Marc Rochkind
设计的有效性语言、语言工具yacc
和lex
,当然还从C
语言中获取了一些优秀的思想。在最初创造Awk
时,其目的是用于文本处理,并且这种语言的基础是,只要在输入数据中有模式匹配,就执行一系列指令。该实用工具扫描文件中的每一行,查找与命令行中所给定内容相匹配的模式。如果发现匹配内容,则进行下一个编程步骤。如果找不到匹配内容,则继续处理下一行。
命令结构
awk [options] 'BEGIN{}{表达式}END{}' file
执行过程
1.命令行赋值 -v
2.执行BEGIN{}里面的内容
3.读取文件第一行
4.进行判断(条件)
1)执行对应的动作
2)继续读取下一行
3)文件结尾
5.最后执行END{}里面的内容
内置变量
$NR 行号
$NF 最后一列
参数说明
- 参数
-vFS
指定字段分隔符,等价于-F
(常用),不指定时默认为空格。
FS: Field Separator 字段分隔符(列)
- 参数
-vRS
指定记录分隔符,不指定时默认为回车\n
。
RS:Record Separator 记录分隔符(行)
- 参数
-vOFS
指定输出分隔符,输出时用输出分隔符来替换默认的字段分隔符,仅在命令行输出内容时显示。
OFS:Output Field Separator 输出分隔符
举个例子
指定字段分隔符
[root@domain ~]# ip a s eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:cd:c1:9f brd ff:ff:ff:ff:ff:ff
inet 104.222.62.32/24 brd 104.2??.???.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::216:3eff:fecd:c19f/64 scope link
valid_lft forever preferred_lft forever
[root@domain ~]# ip a s eth0 | awk -F "[^0-9.]+" 'NR==3{print $2}'
104.222.62.32
指定记录分隔符
[root@domain ~]# awk '{print NR,$0}' passwd.txt | head -n10
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
现在修改记录分隔符(行)来看看效果
[root@domain ~]# awk -vRS=: '{print NR,$0}' passwd.txt | head -n10
1 root
2 x
3 0
4 0
5 root
6 /root
7 /bin/bash
bin
8 x
9 1
可以看出来 awk
命令重新指定:
为行结尾
指定输出分隔符
[root@domain ~]# awk -F ":" -vOFS="@" '$1=$1{print $0}' passwd.txt | head -n10
root@x@0@0@root@/root@/bin/bash
bin@x@1@1@bin@/bin@/sbin/nologin
daemon@x@2@2@daemon@/sbin@/sbin/nologin
adm@x@3@4@adm@/var/adm@/sbin/nologin
lp@x@4@7@lp@/var/spool/lpd@/sbin/nologin
sync@x@5@0@sync@/sbin@/bin/sync
shutdown@x@6@0@shutdown@/sbin@/sbin/shutdown
halt@x@7@0@halt@/sbin@/sbin/halt
mail@x@8@12@mail@/var/spool/mail@/sbin/nologin
operator@x@11@0@operator@/root@/sbin/nologin
运行模式
正则表达式作为模式
例一
cat >>value.list<<'EOF'
#排名 名称 市值(亿美元) 所在国家 行业
NO. NAME VALUE(BillionDollars) Country Industry
01 Apple 8858.88 USA CONSUMER-ELECTRONICS
02 Google 8251.05 USA INTERNET
03 Microsoft 7257.14 USA INTERNET
04 Amazon 6756.09 USA INTERNET
05 Tencent 5724.75 CHN INTERNET
06 Facebook 5517.97 USA INTERNET
07 BerkshireHathaway 5363.00 USA INSURANCE
08 Alibaba 5256.00 CHN INTERNET
09 JPMorganChase 4035.98 USA BANK
10 ICBC 3958.31 CHN BANK
EOF
问题 1.1:打印 Facebook
的所在行
[root@domain ~]# awk '$2~/Facebook/' value.list
06 Facebook 5517.97 USA INTERNET
问题 1.2:打印 Facebook
的排名和市值
[root@domain ~]# awk '$2~/Facebook/{print $1,$3}' value.list
06 5517.97
问题 1.3:显示以A
开头的企业所在行
[root@domain ~]# awk '$2~/^A/' value.list
01 Apple 8858.88 USA CONSUMER ELECTRONICS
04 Amazon 6756.09 USA INTERNET
08 Alibaba 5256.00 CHN INTERNET
问题 1.4:显示以A
开头的企业全名及市值
[root@domain ~]# awk '$2~/^A/{print $2,$3}' value.list
Apple 8858.88
Amazon 6756.09
Alibaba 5256.00
问题 1.5:输出文件的第二行和第三行
[root@domain ~]# awk 'NR==2' value.list
NO. NAME VALUE(BillionDollars) Country Industry
[root@domain ~]# awk 'NR==3' value.list
01 Apple 8858.88 USA CONSUMER ELECTRONICS
[root@domain ~]# awk 'NR==3{print $0}' value.list
01 Apple 8858.88 USA CONSUMER ELECTRONICS
[root@domain ~]# awk 'NR==3{print}' value.list
01 Apple 8858.88 USA CONSUMER ELECTRONICS
小贴士:此例题可以看出$0
为整行,且$0
。
问题 1.6:打印每个公司的市值,去除小数点且每个值时都有以B$
结尾,如8858B$
。
[root@domain ~]# awk '{gsub(/\..*/,"B$",$3);print}' value.list | column -t
#排名 名称 市值(亿美元) 所在国家 行业
NO. NAME VALUE(BillionDollars) Country Industry
01 Apple 8858B$ USA CONSUMER ELECTRONICS
02 Google 8251B$ USA INTERNET
03 Microsoft 7257B$ USA INTERNET
04 Amazon 6756B$ USA INTERNET
05 Tencent 5724B$ CHN INTERNET
06 Facebook 5517B$ USA INTERNET
07 BerkshireHathaway 5363B$ USA INSURANCE
08 Alibaba 5256B$ CHN INTERNET
09 JPMorganChase 4035B$ USA BANK
10 ICBC 3958B$ CHN BANK
小贴士:awk
内置函数gsub
gsub == global substitute 全局替换;column -t
可以使得列对齐(使用空格)美化输出结果。
小结
在 `awk` 中使用正则表达式作为条件,也可以用 `$列~` 的形式用来精准匹配。
`'//'` 寻找字符
`'$3~//'` 在第三列中寻找字符
`~` 匹配
`!~` 不匹配
例二
cat >>donate.txt<<"EOF"
TOM :334:224
CAIN :776:633
JANE :375:200
EOF
问题 2.1:将:
全部换成$
[root@domain ~]# awk '{gsub(/:/,"$",$NF);print}' donate.txt
TOM $334$224
CAIN $776$633
JANE $375$200
小贴士:gsub(/要找的内容/,"替换为什么",列)
全局替换格式。
比较表达式为模式
例三
问题 3.1:打印 value.list 中三行后的内容(排除前方的说明内容)
[root@domain ~]# awk 'NR>=3{print}' value.list
01 Apple 8858.88 USA CONSUMER ELECTRONICS
02 Google 8251.05 USA INTERNET
03 Microsoft 7257.14 USA INTERNET
04 Amazon 6756.09 USA INTERNET
05 Tencent 5724.75 CHN INTERNET
06 Facebook 5517.97 USA INTERNET
07 BerkshireHathaway 5363.00 USA INSURANCE
08 Alibaba 5256.00 CHN INTERNET
09 JPMorganChase 4035.98 USA BANK
10 ICBC 3958.31 CHN BANK
提示:其他可用比较表达式>
、>=
、<
、<=
、==
、!=
指定范围模式
例四
cat >nginx.conf<<'EOF'
server
{
listen 80;
server_name domain1.com www.domain1.com;
access_log logs/domain1.access.log main;
root html;
}
server
{
listen 80;
server_name domain2.com www.domain2.com;
access_log logs/domain2.access.log main;
}
EOF
问题 4.1:显示 nginx.conf
中从{
开始到}
结束的行
[root@domain ~]# awk '/{/,/}/' nginx.conf
{
listen 80;
server_name domain1.com www.domain1.com;
access_log logs/domain1.access.log main;
root html;
}
{
listen 80;
server_name domain2.com www.domain2.com;
access_log logs/domain2.access.log main;
}
BEGIN{} / END{} 模式
BEGIN{} 在读取文件内容之前执行里面的内容
一般用于测试(计算)
设置或修改awk内置变量
awk -F:
awk -vFS=:
awk 'BEGIN{FS=":"}'
awk -vOFS=:
awk 'BEGIN{OFS=":"}'
END{} 处理完文件内容后执行里面的内容
一般用于显示最终结果
例五
问题 5.1:统计 /etc/serivces
文件中空行的数量
[root@domain ~]# awk '/^$/{i=i+1}END{print i}' /etc/services
16
问题 5.2:统计 /etc/passwd
中虚拟用户的数量(以 /sbin/nologin
为命令解释器)(不同系统下可能结果不同)
[root@domain ~]# awk -F: '$NF=="/sbin/nologin"' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
···
省略
···
[root@domain ~]# awk '/nologin$/{i++}END{print i}' /etc/passwd
33
# 用 grep 验证一下
[root@domain ~]# grep '/sbin/nologin$' /etc/passwd | wc -l
33
例六
问题 6.1:统计某文件的某列的总和
[root@domain ~]# seq 10 | awk '{sum=sum+$1;print sum}'
1
3
6
10
15
21
28
36
45
55
可用看出来每次循环都会计算并输出一次结果。
[root@domain ~]# seq 10 | awk '{sum=sum+$1}END{print sum}'
55
例七
问题 7.1:统计 NGINX 访问日志 access.log
中第10列(页面大小)的总和,可用来粗略统计流量使用情况。
[root@domain ~]# awk '{sum=sum+$10}END{print sum}' access.log
2478496663
[root@domain ~]# awk '{sum=sum+$10}END{print sum/1024^3" GB"}' access.log
2.30828 GB
附表:例题练习文件点此下载
[root@domain ~]# head -2 access.log
101.226.61.184 - - [22/Nov/2015:11:02:00 +0800] "GET /mobile/sea-modules/gallery/zepto/1.1.3/zepto.js HTTP/1.1" 200 24662 "http://m.papaonline.com.cn/mobile/theme/ppj/home/index.html" "Mozilla/5.0 (Linux; U; Android 5.1.1; zh-cn; HUAWEI CRR-UL00 Build/HUAWEICRR-UL00) AppleWebKit/533.1 (KHTML, like Gecko)Version/4.0 MQQBrowser/5.4 TBS/025478 Mobile Safari/533.1 MicroMessenger/6.3.7.51_rbb7fa12.660 NetType/3gnet Language/zh_CN"
101.226.61.184 - - [22/Nov/2015:11:02:00 +0800] "GET /mobile/theme/ppj/common/js/baiduAnalytics.js HTTP/1.1" 200 526 "http://m.papaonline.com.cn/mobile/theme/ppj/home/index.html" "Mozilla/5.0 (Linux; U; Android 5.1.1; zh-cn; HUAWEI CRR-UL00 Build/HUAWEICRR-UL00) AppleWebKit/533.1 (KHTML, like Gecko)Version/4.0 MQQBrowser/5.4 TBS/025478 Mobile Safari/533.1 MicroMessenger/6.3.7.51_rbb7fa12.660 NetType/3gnet Language/zh_CN"
#第1列 IP地址
#第4-5列 时间
#第6列 请求方式
#第7列 访问的页面
#第8列 使用协议
#第9列 请求状态
#第10列 请求页面的大小(默认单位字节)
#第11列 来源页面
#第12-18列 浏览器版本、系统类型、渲染引擎等,统称 UA
数组
在 awk
中条件语句可以使用 if
判断或 for
循环,和SEHLL脚本中略有不同。
# awk 中条件语句
if () command;
# awk 中循环语句
for () command;
# awk 中的数组
array[element]
数组名称[元素名称]
例八
cat >url.txt<<'EOF'
http://www.etiantian.org/index.html
http://www.etiantian.org/1.html
http://post.etiantian.org/index.html
http://mp3.etiantian.org/index.html
http://www.etiantian.org/3.html
http://post.etiantian.org/2.html
EOF
问题 8.1:统计以上文本中的三级域名的出现次数
使用常规手段
[root@domain ~]# awk -F"[/.]+" '{print $2 }' url.txt | sort -nr | uniq -ic
3 www
2 post
1 mp3
尝试使用数组进行实现
[root@domain ~]# awk -F "[/.]+" '{h[$2]++;print h["www"]}' url.txt
1
2
2
2
3
3
可以看出来每次检测到行中有www
就会使数组 h[www]
中的数字增加1
[root@domain ~]# awk -F "[/.]+" '{h[$2]++}END{print h["www"],h["post"],h["mp3"]}' url.txt
3 2 1
即可显示全部的三级域名,但是此种方式存在问题,实际统计时需要把全部情况列出来,使用很不方便。可以使用数组自动存储出现的三级域名,以便进行统计。
[root@domain ~]# awk -F "[/.]+" '{h[$2]++}END{for(pol in h) print pol,h[pol]}' url.txt | column -t
www 3
mp3 1
post 2
使用数组pol
存储全部出现过的三级域名,使用h[pol]
存储每个三级域名出现的次数,然后打印即可。
例九
问题 9.1:分析系统登录日志,检查是否存在暴力破解行为,并统计每个IP的破解次数。
附表:例题练习文件点此下载
[root@domain ~]# tail -1 secure-20161219
Dec 19 03:42:01 localhost sshd[9030]: Failed password for root from 59.63.166.84 port 65111 ssh2
先查看一下文件,可以看出登录失败的关键字是 Failed
,而筛选的IP是在 from
与port
之间的。
[root@domain ~]# awk '/Failed/{h[$(NF-3)]++}END{for(pol in h) print pol,h[pol]}' secure-20161219 | sort -rnk2 | head | column -t
218.65.30.25 68652
218.65.30.53 34326
218.87.109.154 21201
112.85.42.103 18065
112.85.42.99 17164
218.87.109.151 17163
218.87.109.150 17163
218.65.30.61 17163
218.65.30.126 17163
218.65.30.124 17163
问题 9.2:分析每个用户被破解的次数
[root@domain ~]# awk '/Failed/{h[$(NF-5)]++}END{for(pol in h) print pol,h[pol]}' secure-20161219 | sort -rnk2 | head | column -t
root 364610
admin 733
user 246
oracle 119
support 104
guest 79
test 70
ubnt 47
pi 41
webadmin 36
例十
问题 10.1:统计 access.log
中,每个页面(url) 所占的总大小并按大小排序。
[root@domain ~]# awk '{size[$7]=size[$7]+$10}END{for (pol in size)print pol,size[pol]}' access.log | sort -nrk2 | head | column -t
/mobile/theme/ppj/home/images/20151111/03.png 115161246
/mobile/theme/ppj/home/images/20151111/04.png 103371446
/mobile/theme/ppj/home/images/20151111/02.png 103021063
/mobile/theme/ppj/home/images/20151111/05.png 82828309
/online/ppjonline/images/product/product_90601.png 66944817
/online/ppjonline/images/product/product_90602.png 66503095
/online/ppjonline/images/product/product_90600.png 65244493
/online/ppjonline/images/ad/20151111/4.png?v=201401020 62519515
/mobile/theme/ppj/home/images/20151111/2.png 62005913
/mobile/theme/ppj/home/images/20151111/01.png 60493565
问题 10.2:统计 access.log
中每个页面出现的次数、总大小、URL名,并按次数进行排序。
[root@domain ~]# awk '{size[$7]=size[$7]+$10;count[$7]++}END{for (pol in size)print count[pol],size[pol],pol}' access.log | sort -k1nr | head | column -t
4838 37176198 /online/api/mc/cart/new/getCart.json
3859 254694 /online/api/mc/sys/nowTime.json
2445 176320 /online/mc/crm/integration/points/pointBalance.json
1872 2061750 /online/api/mc/cart/save.json
1797 86208 /ccbs/global/commonPage/includeHead/contextPath.jsp
1548 895893 /mobile/theme/ppj/account/tpl/footerTpl.html
1344 2402180 /online/api/mc/productCategory/children.json?language=zh_CN&productCategoryCode=ONLINE_SPECIAL_MENU
912 699386 /mobile/theme/ppj/product/tpl/productCategoryTpl.html
838 368120 /ccbs/global/scripts/jquery/plugins/images/loading.gif
问题 10.3:统计 access.log
中每个IP的访问次数,并按照次数进行排序,显示前十位。
[root@domain ~]# awk '{t[$1]++}END{for (pol in t) print pol, t[pol]}' access.log | sort -nrk2 | head | column -t
58.220.223.62 12049
112.64.171.98 10856
114.83.184.139 1982
117.136.66.10 1662
115.29.245.13 1318
223.104.5.197 961
116.216.0.60 957
180.111.48.14 939
223.104.5.202 871
223.104.4.139 869
问题 10.4:统计 access.log
中访问次数最多的图片资源并统计其占用的网络流量。
[root@domain ~]# awk '$7~/bmp|png|jpg|gif/{n[$7]+=1;s[$7]=s[$7]+$10}END{for (i in n) print n[i],s[i],i}' access.log | sort -nr | head| column -t
838 368120 /ccbs/global/scripts/jquery/plugins/images/loading.gif
614 2713447 /mobile/theme/ppj/home/images/placeholder.jpg
540 5783412 /online/ppjonline/images/product_category/product_category_1145.jpg
518 9656178 /mobile/static/common/src/loadingimg.gif
509 82828309 /mobile/theme/ppj/home/images/20151111/05.png
504 14146968 /online/ppjonline/images/product/product_90968.png
498 115161246 /mobile/theme/ppj/home/images/20151111/03.png
497 103021063 /mobile/theme/ppj/home/images/20151111/02.png
493 60493565 /mobile/theme/ppj/home/images/20151111/01.png
489 103371446 /mobile/theme/ppj/home/images/20151111/04.png
问题 10.5:统计 access.log
中每分钟uri请求次数最多的前五个时间点(按请求次数排序)。
[root@domain ~]# awk '{time=substr($4,2,11)" "substr($4,14,5);n[time" "$7]++}END{for(i in n)print i,n[i]}' access.log |sort -rnk4 | head -5 | column -t
22/Nov/2015 11:37 /online/api/mc/cart/new/getCart.json 151
22/Nov/2015 11:29 /online/api/mc/cart/new/getCart.json 117
22/Nov/2015 11:38 /online/api/mc/cart/new/getCart.json 116
22/Nov/2015 11:38 /online/api/mc/sys/nowTime.json 110
22/Nov/2015 11:34 /online/api/mc/cart/new/getCart.json 109
附录
参考链接
本文由 柒 创作,采用 知识共享署名4.0
国际许可协议进行许可。
转载本站文章前请注明出处,文章作者保留所有权限。
最后编辑时间: 2018-07-10 19:58 PM