linux文本处理-awk

文本处理三剑客之 awk

awk 工作原理和基本用法说明

awk:Aho, Weinberger, Kernighan,报告生成器,格式化文本输出,GNU/Linux发布的AWK目前由自由软件基金会(FSF)进行开发和维护,通常也称它为 GNU AWK

有多种版本:

  • AWK:原先来源于 AT & T 实验室的的AWK
  • NAWK:New awk,AT & T 实验室的AWK的升级版
  • GAWK:即GNU AWK。所有的GNU/Linux发布版都自带GAWK,它与AWK和NAWK完全兼容

gawk:模式扫描和处理语言,可以实现下面功能

  • 文本处理
  • 输出格式化的文本报表
  • 执行算数运算
  • 执行字符串操作

格式:

awk [options]   'program'     var=value   file…
awk [options]   -f programfile    var=value  file…

说明:

program通常是被放在单引号中,并可以由三种部分组成

  • BEGIN语句块
  • 模式匹配的通用语句块
  • END语句块

常见选项:

  • -F “分隔符” 指明输入时用到的字段分隔符
  • -v var=value 变量赋值

Program格式:

pattern{action statements;..}

pattern:决定动作语句何时触发及触发事件,比如:BEGIN,END
action statements:对数据进行处理,放在{}内指明,常见:print, printf

awk 工作过程

awk

第一步:执行BEGIN{action;… }语句块中的语句
第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{ action;… }语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
第三步:当读至输入流末尾时,执行END{action;…}语句块
BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中
END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块
pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块

分割符、域和记录

  • 文件的每一行称为记录record
  • 如果省略action,则默认执行 print ​/$0 的操作

常用的action分类

  • output statements:print,printf
  • Expressions:算术,比较表达式等
  • Compound statements:组合语句
  • Control statements:if, while等
  • input statements

awk控制语句

  • { statements;… } 组合语句
  • if(condition) {statements;…}
  • if(condition) {statements;…} else {statements;…}
  • while(conditon) {statments;…}
  • do {statements;…} while(condition)
  • for(expr1;expr2;expr3) {statements;…}
  • break
  • continue
  • exit
动作 print

格式:

print item1, item2, ...

说明:

  • 逗号分隔符
  • 输出item可以字符串,也可是数值;当前记录的字段、变量或awk的表达式
  • 如省略item,相当于print $0

范例:

[root@centos8 ~]#awk    '{print "hello,awk"}'
[root@centos8 ~]#awk -F: '{print "wang"}' /etc/passwd
[root@centos8 ~]#awk -F: '{print}' /etc/passwd
[root@centos8 ~]#awk -F: '{print 0}' /etc/passwd
[root@centos8 ~]#awk -F: '{print1,3}' /etc/passwd
[root@centos8 ~]#awk -F: '{print1"/t"3}' /etc/passwd
[root@centos8 ~]#grep "^UUID" /etc/fstab |awk {'print2,3'}
/ xfs
/boot ext4
/data xfs
swap swap
[root@centos8 ~]#awk '{print1}' access_log |sort |uniq -c|sort -nr|head 
   4870 172.20.116.228
   3429 172.20.116.208
   2834 172.20.0.222
   2613 172.20.112.14
   2267 172.20.0.227
   2262 172.20.116.179
   2259 172.20.65.65
   1565 172.20.0.76
   1482 172.20.0.200
   1110 172.20.28.145

[root@centos8 ~]#df | awk -F"[[:space:]]+|%" '{print 5}'
Use
0
0
1
0
5
1
92
1
[root@centos8 ~]#df | grep "^/dev/sd" | awk -F"[[:space:]]+|%" '{print5}'
5
1
92

[root@centos8 ~]#df |  awk -F"[[:space:]]+|%" '/^//dev//sd/{print $5}'
5
1
92
awk变量

awk中的变量分为:内置和自定义变量

常见的内置变量

FS:输入字段分隔符,默认为空白字符,功能相当于 -F

范例:

awk -v FS=':'  '{print 1,FS,3}' /etc/passwd
awk -v FS=":" '{print 1FS3}' /etc/passwd
awk –F:   '{print 1,3,7}'   /etc/passwd    
S=:;awk -v FS=S '{print 1FS3}' /etc/passwd

OFS:输出字段分隔符,默认为空白字符

范例:

awk  -v FS=':'  -v OFS=':'  '{print 1,3,$7}'   /etc/passwd

RS:输入记录分隔符,指定输入时的换行符

范例:

awk -v RS=' ' ‘{print }’ /etc/passwd

ORS:输出记录分隔符,输出时用指定符号代替换行符

范例:

awk -v RS=' ' -v ORS='###'  '{print $0}' /etc/passwd

NF:字段数量

范例:

#引用变量时,变量前不需加[root@centos8 ~]#awk  -F:'{print NF}'     /etc/fstab  
[root@centos8 ~]#awk  -F:'{print(NF-1)}'  /etc/passwd
[root@centos8 ~]#ls /misc/cd/BaseOS/Packages/*.rpm |awk -F"." '{print (NF-1)}'|sort |uniq -c
    389 i686
    208 noarch
   1060 x86_64
[root@centos8 ~]#ss -nt |grep "^ESTAB" | awk -F"[[:space:]]+|:" '{print(NF-2)}'
10.0.0.1
10.0.0.7
10.0.0.1
[root@centos8 ~]#ss -nt |awk -F"[[:space:]]+|:" '/^ESTAB/{print $(NF-2)}'

范例: 将连接数超过3个以上的IP放入黑名单拒绝访问

cat deny_dos.sh
while true;do
ss -nt |grep "^ESTAB" | awk -F"[[:space:]]+|:" '{print (NF-2)}'|sort |uniq -c|while read
count ip;do
#ss -nt | awk -F"[[:space:]]+|:" '/^ESTAB/{print(NF-2)}'|sort |uniq -c
    if [ count -gt 3 ];then
       iptables -A INPUT -sip -j REJECT
    fi
done
sleep 10
done

NR:记录编号

范例:

[root@centos8 ~]#awk -F: '{print NR}' /etc/passwd
1
2
3
.......
[root@centos8 ~]#awk -F: 'END{print NR}' /etc/passwd
57
[root@centos8 ~]#awk -F: 'BEGIN{print NR}' /etc/passwd
0

FNR:各文件分别计数,记录号

范例:

awk '{print FNR}'  /etc/fstab /etc/inittab

[root@centos8 ~]#awk  '{print NR,0}' /etc/issue /etc/redhat-release 
1 /S
2 Kernel /r on an /m
3 
4 CentOS Linux release 8.0.1905 (Core) 
[root@centos8 script40]#awk  '{print FNR,0}' /etc/issue /etc/redhat-release 
1 /S
2 Kernel /r on an /m
3 
1 CentOS Linux release 8.0.1905 (Core)

FILENAME:当前文件名

范例:

[root@centos8 ~]#awk '{print FILENAME}'  /etc/fstab

[root@centos8 ~]#awk  '{print FNR,FILENAME,$0}' /etc/issue /etc/redhat-release 
1 /etc/issue /S
2 /etc/issue Kernel /r on an /m
3 /etc/issue 
1 /etc/redhat-release CentOS Linux release 8.0.1905 (Core)

ARGC:命令行参数的个数

范例:

[root@centos8 ~]#awk '{print ARGC}'  /etc/issue /etc/redhat-release 
3
3
3
3
[root@centos8 ~]#awk 'BEGIN{print ARGC}'  /etc/issue /etc/redhat-release 
3

ARGV:数组,保存的是命令行所给定的各参数

范例:

[root@centos8 ~]#awk 'BEGIN{print ARGV[0]}'  /etc/issue /etc/redhat-release 
awk
[root@centos8 ~]#awk 'BEGIN{print ARGV[1]}'  /etc/issue /etc/redhat-release 
/etc/issue
[root@centos8 ~]#awk 'BEGIN{print ARGV[2]}'  /etc/issue /etc/redhat-release 
/etc/redhat-release
[root@centos8 ~]#awk 'BEGIN{print ARGV[3]}'  /etc/issue /etc/redhat-release 

[root@centos8 ~]#

自定义变量(区分字符大小写)

  • -v var=value
  • 在program中直接定义

范例:

awk  -v test='hello gawk' '{print test}' /etc/fstab 
awk  -v test='hello gawk' 'BEGIN{print test}' 
awk  'BEGIN{test="hello,gawk";print test}' 
awk  -F: '{sex=“male”;print 1,sex,age;age=18}' /etc/passwd

cat awkscript
{print script,1,$2}
awk  -F: -f awkscript script="awk" /etc/passwd

5.3.4 动作 printf

printf 可以实现格式化输出

格式:

printf “FORMAT”, item1, item2, ...

说明:

  • 必须指定FORMAT
  • 不会自动换行,需要显式给出换行控制符,/n
  • FORMAT中需要分别为后面每个item指定格式符

格式符:与item一一对应

  • %c:显示字符的ASCII码
  • %d, %i:显示十进制整数
  • %e, %E:显示科学计数法数值
  • %f:显示为浮点数
  • %g, %G:以科学计数法或浮点形式显示数值
  • %s:显示字符串
  • %u:无符号整数
  • %%:显示%自身

修饰符

#[.#]   第一个数字控制显示的宽度;第二个#表示小数点后精度,如:%3.1f
-       左对齐(默认右对齐) 如:%-15s
+       显示数值的正负符号   如:%+d

范例:

awk -F:   '{printf "%s",1}' /etc/passwd
awk -F:   '{printf "%s/n",1}' /etc/passwd
awk -F:   '{printf "%-20s %10d/n",1,3}' /etc/passwd
awk -F:   '{printf "Username: %s/n",1}'  /etc/passwd
awk -F:   '{printf “Username: %sUID:%d/n",1,3}'    /etc/passwd
awk -F:   '{printf "Username: %25sUID:%d/n",1,3}' /etc/passwd
awk -F:   '{printf "Username: %-25sUID:%d/n",1,$3}'    /etc/passwd
操作符

算术操作符:

x+y, x-y, x*y, x/y, x^y, x%y
-x:转换为负数
+x:将字符串转换为数值

字符串操作符:没有符号的操作符,字符串连接

赋值操作符:

=, +=, -=, *=, /=, %=, ^=,++, --

范例:

[root@centos8 ~]#awk  'BEGIN{i=0;print ++i,i}'
1 1
[root@centos8 ~]#awk  'BEGIN{i=0;print i++,i}'
0 1

比较操作符:

==, !=, >, >=, <, <=

模式匹配符:

~   左边是否和右边匹配,包含关系
!~  是否不匹配

范例:

[root@centos8 ~]#awk  -F: '0 ~ /root/{print1}'  /etc/passwd
[root@centos8 ~]#awk  -F: '0 ~ "^root"{print1}'  /etc/passwd
[root@centos8 ~]#awk  '0  !~ /root/'   /etc/passwd
[root@centos8 ~]#awk  '/root/'   /etc/passwd
[root@centos8 ~]#awk  -F: '3==0'     /etc/passwd
[root@centos8 ~]#df |  awk -F"[[:space:]]+|%" '0 ~ /^//dev//sd/{print5}'
5
1
92

[root@centos8 ~]#ifconfig  eth0 | awk 'NR==2{print $2}'
10.0.0.8

逻辑操作符:

与&&,或||,非!

范例:

awk -F:   '3>=0 &&3<=1000 {print 1}'  /etc/passwd
awk -F:   '3==0 || 3>=1000 {print1}'  /etc/passwd 
awk -F:   '!(3==0) {print1}'     /etc/passwd
awk -F:   '!(3>=500) {print3}' /etc/passwd

条件表达式(三目表达式)

selector?if-true-expression:if-false-expression

范例:

awk -F: '{3>=1000?usertype="Common User":usertype="SysUser";printf "%-20s:%12s/n",1,usertype}'   /etc/passwd
模式PATTERN

PATTERN:根据pattern条件,过滤匹配的行,再做处理

  1. 如果未指定:空模式,匹配每一行

范例:

[root@centos8 ~]#awk -F: '{print 1,3}' /etc/passwd
  1. /regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来

范例:

[root@centos8 ~]#awk   '/^UUID/{print 1}'     /etc/fstab
[root@centos8 ~]#awk   '!/^UUID/{print1}'   /etc/fstab
[root@centos8 ~]#df  | awk '/^//dev//sd/'
/dev/sda2      104806400 4935924  99870476   5% /
/dev/sda3       52403200  398876  52004324   1% /data
/dev/sda1         999320  848572     81936  92% /boot
  1. relational expression: 关系表达式,结果为“真”才会被处理
    真:结果为非0值,非空字符串
    假:结果为空字符串或0值

范例:

awk   -F:  'i=1;j=1{print i,j}' /etc/passwd
awk  '!0'  /etc/passwd ;awk  '!1'   /etc/passwd
Awk  -F: '3>=1000{print1,3}'  /etc/passwd
awk  -F: '3<1000{print 1,3}'  /etc/passwd
awk  -F: 'NF=="/bin/bash"{print1,NF}' /etc/passwd
awk  -F: 'NF ~ /bash/{print1,$NF}' /etc/passwd
  1. line ranges:行范围

/pat1/,/pat2/ 不支持直接给出数字格式

范例:

[root@centos8 ~]#awk '/^bin/,/^adm/' /etc/passwd
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
[root@centos8 ~]#sed  -n '/^bin/,/^adm/p' /etc/passwd
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

[root@centos8 ~]#awk 'NR>=3 && NR<=6{print NR,$0}' /etc/passwd
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

[root@centos8 ~]#sed -n '3,6p' /etc/passwd
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
  1. BEGIN/END模式

    BEGIN{}:仅在开始处理文件中的文本之前执行一次

    END{}:仅在文本处理完成之后执行一次

范例

awk -F : 'BEGIN {print "USER USERID"} {print 1":"3} END{print "END FILE"}' /etc/passwd
awk -F: '{print "USER USERID";print 1":"3} END{print "END FILE"}'  /etc/passwd
awk -F: 'BEGIN{print "USER  UID  /n--------------- "}{print 1,3}'  /etc/passwd
awk -F: 'BEGIN{print "USER UID  /n------"}{print 1,3}'END{print "========"} /etc/passwd
seq 10 | awk   'i=0'
seq 10 | awk   'i=1'
seq 10 | awk   'i=!i'
seq 10 | awk   '{i=!i;print i}'
seq 10 | awk   '!(i=!i)'              
seq 10 | awk  -v  i=1 'i=!i'
条件判断 if-else

语法:

if(condition){statement;…}[else statement]
if(condition1){statement1}else if(condition2){statement2}else{statement3}

使用场景:对awk取得的整行或某个字段做条件判断

范例:

awk -F: '{if(3>=1000)print1,3}' /etc/passwd
awk -F: '{if(NF=="/bin/bash") print 1}' /etc/passwd
awk '{if(NF>5) print0}' /etc/fstab
awk -F: '{if(3>=1000) {printf "Common user: %s/n",1} else {printf "root or Sysuser: %s/n",1}}' /etc/passwd
awk -F: '{if(3>=1000) printf "Common user: %s/n",1; else printf "root or Sysuser: %s/n",1}' /etc/passwd
df -h|awk -F% '/^//dev//sd/{print 1}'| awk 'NF>=80{print 1,5}'
df | awk -F"[[:space:]]+|%" '/^//dev//sd/{if(5>80)print1,$5}'

awk 'BEGIN{ test=100;if(test>90){print "very good"}
    else if(test>60){ print "good"}else{print "no pass"}}'
switch语句

语法:

switch(expression) {case VALUE1 or /REGEXP/: statement1; case VALUE2 or /REGEXP2/: statement2; ...; default: statementn}
循环while

语法:

while (condition) {statement;…}

条件“真”,进入循环;条件“假”,退出循环
使用场景:
对一行内的多个字段逐一类似处理时使用
对数组中的各元素逐一处理时使用

示例:

[root@centos8 ~]#awk 'BEGIN{print length("hello")}'
5
[root@centos8 ~]#awk 'BEGIN{print length("马哥教育")}'
4
[root@centos7 ~]#awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print i,length(i); i++}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-1062.el7.x86_64 31
root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
net.ifnames=0 13
linux16 7
/vmlinuz-0-rescue-b12558570741487c9328c996e3265b09 50
root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
net.ifnames=0 13

[root@centos7 ~]#awk  '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length(i)>=10){printi,length($i)}; i++}}' /etc/grub2.cfg
/vmlinuz-3.10.0-1062.el7.x86_64 31
root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46
crashkernel=auto 16
net.ifnames=0 13
/vmlinuz-0-rescue-b12558570741487c9328c996e3265b09 50
root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46
crashkernel=auto 16
net.ifnames=0 13

[root@centos8 ~]#awk 'BEGIN{ total=0;i=1;while(i<=100){total+=i;i++};print total}'
5050
循环 do-while

语法:

do {statement;…}while(condition)

意义:无论真假,至少执行一次循环体

do-while循环
语法:do {statement;…}while(condition)
意义:无论真假,至少执行一次循环体

范例:

[root@centos8 ~]#awk 'BEGIN{ total=0;i=1;do{ total+=i;i++;}while(i<=100);print total}'
5050
循环for

语法:

for(expr1;expr2;expr3) {statement;…}

常见用法:

for(variable assignment;condition;iteration process) {for-body}

特殊用法:能够遍历数组中的元素

for(var in array) {for-body}

范例:

[root@centos8 ~]#awk 'BEGIN{total=0;for(i=1;i<=100;i++){total+=i};print total}'
5050

范例:

[root@centos7 ~]#awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print i,length(i)}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-1062.el7.x86_64 31
root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
net.ifnames=0 13
linux16 7
/vmlinuz-0-rescue-b12558570741487c9328c996e3265b09 50
root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
net.ifnames=0 13

性能比较

time (awk 'BEGIN{ total=0;for(i=0;i<=10000;i++){total+=i;};print total;}')
time (total=0;for i in {1..10000};do total=((total+i));done;echo total)
time (for ((i=0;i<=10000;i++));do let total+=i;done;echototal)
time (seq –s ”+” 10000|bc)
continue和break

格式:

continue [n]
break [n]

范例:

[root@centos8 ~]#awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i%2==0)continue;sum+=i}print sum}'
2500

[root@centos8 ~]#awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==50)break;sum+=i}print sum}'
1225
next

next 可以提前结束对本行处理而直接进入下一行处理(awk自身循环)

范例:

[root@centos8 ~]#awk -F: '{if(3%2!=0) next; print1,$3}' /etc/passwd
root 0
daemon 2
lp 4
shutdown 6
mail 8
games 12
ftp 14
nobody 65534
polkitd 998
gluster 996
rtkit 172
rpc 32
chrony 994
saslauth 992
clevis 984
pegasus 66
colord 982
setroubleshoot 980
gdm 42
gnome-initial-setup 978
sshd 74
avahi 70
tcpdump 72
wang 1000
数组

awk的数组为关联数组

格式

array[index-expression]

范例:

weekdays["mon"]="Monday" 

index-expression

  • 可使用任意字符串;字符串要使用双引号括起来
  • 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
  • 若要判断数组中是否存在某元素,要使用“index in array”格式进行遍历

范例:

[root@centos8 ~]#awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print weekdays["mon"]}'
Monday

范例:

awk '!line[0]++'  dupfile
awk '{!line[0]++;print 0, line[0]}'  dupfile

范例:判断数组索引是否存在

[root@centos8 ~]# awk 'BEGIN{array["i"]="x"; array["j"]="y" ; print "i" in array, "y" in array }'
1 0
[root@centos8 ~]#awk 'BEGIN{array["i"]="x"; array["j"]="y" ;if ("i" in array ) {print "存在"}else{print "不存在"}}'
存在
[root@centos8 ~]#awk 'BEGIN{array["i"]="x"; array["j"]="y" ;if ("abc" in array ) {print "存在"}else{print "不存在"}}'
不存在

若要遍历数组中的每个元素,要使用for循环

for(var in array) {for-body}

注意:var会遍历array的每个索引

范例:遍历数组

[root@centos8 ~]#awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'
Tuesday
Monday

[root@centos8 ~]#awk 'BEGIN {
a["x"] = "welcome"
a["y"] = "to"
a["z"] = "Magedu"
for (i in a) {
     print i,a[i]
}
}'
x welcome
y to
z Magedu

[root@centos8 ~]#awk -F: '{user[1]=3}END{for(i in user){print "username: "i,"uid: "user[i]}}' /etc/passwd
username: adm uid: 3
username: rpc uid: 32
username: dnsmasq uid: 985
username: radvd uid: 75
username: sync uid: 5
username: mail uid: 8
username: exim uid: 93
username: tss uid: 59
username: gluster uid: 996
username: unbound uid: 995
username: halt uid: 7

范例:

[root@centos8 ~]#cat  ss.log | sed -nr '1!s/^([^0-9]+) .*//1/p'|sort |uniq -c
    529 ESTAB    
      9 LISTEN   
    128 SYN-RECV 
     95 TIME-WAIT
[root@centos8 ~]#ss -ant | awk 'NR!=1{state[1]++}END{for(i in state){print i,state[i]}}'
SYN-RECV 128
LISTEN 9
ESTAB 529
TIME-WAIT 95

[root@centos8 ~]#netstat -tan | awk '/^tcp/{state[NF]++}END{for(i in state){print i,state[i]}}'
LISTEN 9
SYN_RECV 126
ESTABLISHED 523
FIN_WAIT2 40

[root@centos8 ~]#awk '{ip[1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log
172.20.0.200 1482
172.20.21.121 2
172.20.30.91 29
172.16.102.29 864
172.20.0.76 1565
172.20.9.9 15
172.20.1.125 463
172.20.61.11 2
172.20.73.73 198

[root@centos8 ~]#awk '{ip[1]++}END{for(i in ip){print ip[i],i}}' access_log  |sort -nr| head -3
4870 172.20.116.228
3429 172.20.116.208
2834 172.20.0.222
[root@centos8 ~]#awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' access_log |sort -k2 -nr|head -3
172.20.116.228 4870
172.20.116.208 3429
172.20.0.222 2834

范例:多维数组

[root@centos8 ~]#awk 'BEGIN{
> array[1][1]=11
> array[1][2]=12
> array[1][3]=13
> array[2][1]=21
> array[2][2]=22
> array[2][3]=23
> for (i in array)
>     for (j in array[i])
>         print array[i][j]
> }'
11
12
13
21
22
23
awk函数

awk 的函数分为内置和自定义函数

常见内置函数

数值处理:
rand():返回0和1之间一个随机数

srand():配合rand() 函数,生成随机数的种子

int():返回整数

范例:

[root@centos8 ~]#awk 'BEGIN{srand();print rand()}'
0.790437
[root@centos8 ~]#awk 'BEGIN{srand();print rand()}'
0.283736
[root@centos8 ~]#awk 'BEGIN{srand();print rand()}'
0.948082
[root@centos8 ~]#awk 'BEGIN{srand();print rand()}'
0.371798

[root@centos8 ~]#awk  'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }' 
35
17
35
95
19
15
70
54
46
93

字符串处理:

length([s]):返回指定字符串的长度

sub(r,s,[t]):对t字符串搜索r表示模式匹配的内容,并将第一个匹配内容替换为s

范例:

[root@centos8 ~]#echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",1)'
2008-08:08 08:08:08

[root@centos8 ~]#echo "2008:08:08 08:08:08" | awk '{sub(/:/,"-",1);print $0}'
2008-08:08 08:08:08

gsub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表示的内容

范例:

[root@centos8 ~]#echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",0)'
2008-08-08 08-08-08

[root@centos8 ~]#echo "2008:08:08 08:08:08" | awk '{gsub(/:/,"-",0);print $0}'
2008-08-08 08-08-08

split(s,array,[r]):以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2,…

范例:

[root@centos8 ~]#netstat -tn | awk '/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){print i,count[i]}}'
10.0.0.1 1
10.0.0.6 1
10.0.0.7 673

system 函数:可以awk中调用shell命令
空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了awk的变量外其他一律用""引用起来

awk 'BEGIN{system("hostname")}' 
awk 'BEGIN{score=100; system("echo  your score is " score) }'

[root@centos8 ~]#netstat -tn | awk '/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){if(count[i]>=10){system("iptables -A INPUT -s "i" -j REJECT")}}}'
自定义函数

自定义函数格式:

function name ( parameter, parameter,  ... ) {
   statements
   return expression
}

范例:

[root@centos8 ~]#cat func.awk
function max(x,y) {
    x>y?var=x:var=y
    return var
}
BEGIN{print max(a,b)}
[root@centos8 ~]#awk  -v a=30 -v b=20 -f func.awk
30
awk脚本

将awk程序写成脚本,直接调用或执行

范例:

[root@centos8 ~]#cat passwd.awk 
{if(3>=1000)print1,$3}
[root@centos8 ~]#awk -F: -f passwd.awk /etc/passwd
nobody 65534
wang 1000
mage 1001

范例:

[root@centos8 ~]#cat test.awk
#!/bin/awk -f
#this is a awk script
{if(3>=1000)print1,$3}
[root@centos8 ~]#chmod +x test.awk
[root@centos8 ~]#./test.awk -F: /etc/passwd
nobody 65534
wang 1000
mage 1001

向awk脚本传递参数

格式:

awkfile  var=value var2=value2... Inputfile

注意:在BEGIN过程中不可用。直到首行输入完成以后,变量才可用。可以通过-v 参数,让awk在执行BEGIN之前得到变量的值。命令行中每一个指定的变量都需要一个-v参数

范例:

[root@centos8 ~]#cat test2.awk 
#!/bin/awk -f
{if(3 >=min &&3<=max)print 1,3} 
[root@centos8 ~]#chmod +x test2.awk
[root@centos8 ~]#./test2.awk  -F: min=100 max=200 /etc/passwd
systemd-resolve 193
rtkit 172
pulse 171
qemu 107
usbmuxd 113
abrt 173

练习

1、文件host_list.log 如下格式,请提取”.magedu.com”前面的主机名部分并写入到回到该文件中

1 www.magedu.com
2 blog.magedu.com
3 study.magedu.com
4 linux.magedu.com
5 python.magedu.com
......
999 study.magedu.com

参考答案:

[root@centos8 ~]#cat host_list.log
1 www.magedu.com
2 blog.magedu.com
3 study.magedu.com
4 linux.magedu.com
5 python.magedu.com
[root@centos8 ~]#awk -F"[ .]"  '{print 2}' host_list.log
www
blog
study
linux
python
[root@centos8 ~]#awk -F"[ .]"  '{print2}' host_list.log >> host_list.log 
[root@centos8 ~]#cat host_list.log
1 www.magedu.com
2 blog.magedu.com
3 study.magedu.com
4 linux.magedu.com
5 python.magedu.com
www
blog
study
linux
python

2、统计/etc/fstab文件中每个文件系统类型出现的次数
3、统计/etc/fstab文件中每个单词出现的次数
4、提取出字符串Yd$C@M05MB%9&Bdh7dq+YVixp3vpw中的所有数字
5、有一文件记录了1-100000之间随机的整数共5000个,存储的格式100,50,35,89…请取出其中最大和最小的整数
6、解决DOS攻击生产案例:根据web日志或者或者网络连接数,监控当某个IP并发连接数或者短时内PV达到100,即调用防火墙命令封掉对应的IP,监控频率每隔5分钟。防火墙命令为:iptables -A INPUT -s IP -j REJECT

7、将以下文件内容中FQDN取出并根据其进行计数从高到低排序

http://mail.magedu.com/index.html
http://www.magedu.com/test.html
http://study.magedu.com/index.html
http://blog.magedu.com/index.html
http://www.magedu.com/images/logo.jpg
http://blog.magedu.com/20080102.html
http://www.magedu.com/images/magedu.jpg

参考答案:

[root@centos8 ~]#awk -F"/" '{url[$3]++}END{for(i in url){print url[i],i}}' url.log |sort -nr
3 www.magedu.com
2 blog.magedu.com
1 study.magedu.com
1 mail.magedu.com

8、将以下文本以inode为标记,对inode相同的counts进行累加,并且统计出同一inode中,beginnumber的最小值和endnumber的最大值

inode|beginnumber|endnumber|counts|
106|3363120000|3363129999|10000|
106|3368560000|3368579999|20000|
310|3337000000|3337000100|101|
310|3342950000|3342959999|10000|
310|3362120960|3362120961|2|
311|3313460102|3313469999|9898|
311|3313470000|3313499999|30000|
311|3362120962|3362120963|2|

输出的结果格式为:

310|3337000000|3362120961|10103|
311|3313460102|3362120963|39900|
106|3363120000|3368579999|30000|

本文链接:http://www.yunweipai.com/33891.html

原创文章,作者:kepupublish,如若转载,请注明出处:https://blog.ytso.com/52421.html

(0)
上一篇 2021年8月6日
下一篇 2021年8月6日

相关推荐

发表回复

登录后才能评论