awk是一种可以处理数据、产生格式化报表的语言,功能相当强大。awk的工作方式是读取数据文件,将每一行数据视为一条记录(record),每笔记录以字段分隔符分成若干字段,然后输出各个字段的值。
awk对每一条记录,都会套用一个"样式{操作}",如果该行符合样式,就执行指定的操作。样式或操作之一,可以省略。如果只有样式,表示要显示符合样式的数据行;如果只有操作,表示对每一数据行都执行该项操作。
以下是awk常用的作用格式:
awk"样式"文件:把符合样式的数据行显示出来。
awk '{操作}' 文件:对每一行都执行{}中的操作。
awk '样式{操作}' 文件:对符合样式的数据行,执行{}中的操作。
简单用法:
awk '/正则/{print $1,$3}' file
awk -F'[:,]' '/root/{print $1,$3}' passwd #多分隔符
awk -F'[:,]' '$n~/正则/{print $1}' passwd ~字符等于
awk -F'[:,]' '$1~/root/{print $0}' passwd
root:x,0:0:root:/root:/bin/bash
~ 匹配,与==相比不是精确比较
!~ 不匹配,不精确比较
== 等于,必须全部相等,精确比较
!= 不等于,精确比较
&& 逻辑与 前面正确了执行
|| 逻辑或 前面错误了执行
+ 匹配时表示1个或1个以上
== 数字等于
>= 数字大于等于
<= 数字小于等于
awk -F'[:,]' '$3==0 {print $0}' passwd
root:x,0:0:root:/root:/bin/bash ==数字等于 >=数字等于 <=数字小于
awk -F'[:,]' '/^root/,$3<2 {print $0}' passwd
root:x,0:0:root:/root:/bin/bash 以root开头切$3小于2 打印出来
awk多个判断
awk -F'[,:]' '$3<1000 &&$3>500 {print $1 " this is system user"}' passwd
awk双条件判断
awk -F'[:,]' '/^root/||$3<2 {print $0}' passwd
root:x,0:0:root:/root:/bin/bash ||或者
bin:x:1:1:bin:/bin:/sbin/nologin
awk -F'[:,]' '/^root/&&$3<2 {print $0}' passwd
root:x,0:0:root:/root:/bin/bash && and
awk -F'[,:]' '$3>500&&$3<1000{print $0}' passwd $3 小于1000大于500
admin:x:600:600::/export:/bin/bash
mysql:x:800:800::/home/mysql:/bin/bash
www:x:801:801::/home/www:/sbin/nologin
git:x:802:802::/home/git:/bin/bash
awk -F'[,:]' '$5 ~/root/ ||$3<2 {print $0}' passwd $5等于root或者$3小于2
root,x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
awk -F'[,:]' '$1 ~ /<r/ {print $0}' passwd 单词铆钉
awk -F'[:,]' '$5 !~ /r/{print $0}' passwd 不等于
awk -F'[,:]' '$3==10{print $0}' passwd 等于精确匹配
awk -F'[,:]' '$1=="ro"{print $0}' passwd
awk -F'[,:]' '$1=="root"{print $0}' passwd
root,x:0:0:root:/root:/bin/bash 等于精确匹配
awk -F'[,:]' '{$3+=2;print $3}' passwd ++$3从2开始每次加1
==========================================================================
awk 打印区间 root和www开头的行
awk -F'[,:]' '/^ro/,/^ww/' passwd
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
admin:x:600:600::/export:/bin/bash
mysql:x:800:800::/home/mysql:/bin/bash
www:x:801:801::/home/www:/sbin/nologin
==========================================================================
awk 'BEGIN{}{} END{}' file
awk 'BEGIN{}/模式/{操作}' file
awk -F'[,:]' 'BEGIN{print "-----------------------------------------------"}$1~/root/{print $1}' passwd
-----------------------------------------------
root
先执行 BEGING 打印出来-------- 然后匹配模式 $1=root 得 打印出第一段来
awk内置变量
FS=变量
属性 说明
$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 34
RT 把RS记录分割符设置成一个正则,匹配一个或多个数字的字符段为记录分割符。RT就是当RS为正则表达式时的匹配到的每个记录的分割符的内容即为RT变量表示。那么把RT累加给变量s,然后最后输出该值。达到了把所有文本中出现过的数字相加。这里还有一个小细节不要放过,就是 + 号,该符号在BREs正则表达式中是不支持这样写的正确的写法是 + ,但是awk默认是使用的EREs正则表达式,所以支持 + 号的写法,大家注意区分。
既是RS匹配的内容
awk -F'[:,]' '{print NR,NF,$1,$NF}' passwd
1 7 root /bin/bash
2 7 bin /sbin/nologin
3 7 daemon /sbin/nologin
4 7 adm /sbin/nologin
5 7 lp /sbin/nologin
6 7 sync /bin/sync
7 7 admin /bin/bash
8 7 mysql /bin/bash
9 7 www /sbin/nologin
10 7 git /bin/bash
11 7 redis /bin/bash
12 7 gogs /bin/bash
13 7 abc /bin/bash
14 7 bbb /bin/bash
awk 'BEGIN{FS="[:,]"}$1~/root/{print $0}' passwd
root:x,0:0:root:/root:/bin/bash
awk 'BEGIN{FS="[,:]";"s=0"}{s+=$3}END{print "所有的UID之和是:",s}' passwd
所有的UID之和是: 6577
cat abc.log
1 2 3
2 3 4
求和
awk '{sum+=$3} END {print "Sum = ", sum}' abc.log
Sum = 7
求平均值
awk '{sum+=$3} END {print "Sum = ", sum/NR}' abc.log
Sum = 3.5
==========================================================================
awk循环
if(判断1){语句1}
else if(判断2){语句2}
awk '/模式/{if(判断1){语句1}}' file
awk -F'[:,]' '{if($3==0){print $1 " is root"}else if($3<1000){print $1 " is system user"}else if ($3>=1000){print $1"is common user"}}' /etc/passwd
for循环
for(变量初始值;变量的范围;增长幅度){命令}
for(i=1;i<=10;1++){print $3}
awk -F'[:,]' '{for(i=$3;i<NR;i++) {if($3==0) {print $1 " is root"}else if($3<1000){print $1 " is system user"}else if($3>=1000){print $1 " is common user"}}}' /etc/passwd
root is root
bin is system user
daemon is system user
adm is system user
lp is system user
sync is system user
shutdown is system user
halt is system user
mail is system user
==========================================================================
合并文件
cat a
a aaa 1
b bbb 2
c ccc 3
cat b
a 111 4
b 222 6
c 333 5
awk 'NR==FNR{s[$1]=$0;next}NR!=FNR{print s[$1],$2,$3}' a b
a aaa 1 111 4
b bbb 2 222 6
c ccc 3 333 5
NR==FNR 匹配第一个文件 NR!=FNR 第二个文件
s[$1] 设置一个数组将第一个文件的每行当作数组的下标
next 去除重复
cat a1
tom:001
bob:002
cat b1
001:aa
001:bb
002:cc
002:dd
awk 'BEGIN{FS="[:]"}NR==FNR{s[$2]=$0}NR!=FNR{print s[$1]":"$2}' a1 b1
tom:001:aa
tom:001:bb
bob:002:cc
bob:002:dd
思路 先看两个文件 找到相同处 明显是001 将001作为数组的下标
再次查看发现a文件没有重复的作为数组更好
所以确定 s[$2] 也就是s[001]
NR==FNR{s[$2]=$0} 得到的结果就是
s[001]=tom:001
s[002]=bob:002
所以在后面的操作要把b文件的第二段连接上即可
如果c1的$1和c2的$3相同,c1的$2和c2的$4的后面的三个字符一样,c1的最后一个字段和c2的最后一个字段一样,那么打印c2的这些行
cat c1
AAA 001 1000.00
BBB 001 2000.00
DDD 002 4000.00
EEE 002 5000.00
FFF 003 6000.00
cat c2
01 1111 AAA WW001 $$$$ 1000.00
02 2222 BBB GG001 %%%% 2000.00
03 3333 CCC JJ001 **** 3000.00
04 4444 DDD FF002 &&&& 4000.00
05 5555 EEE RR002 @@@@ 5000.00
06 666 FFF UU003 JJJJ 6000.00
07 777 III II005 PPPP 7000.00
08 8888 TTT TT008 TTTT 8000.00
awk 'NR==FNR{a[$1]=$1$2$3}NR!=FNR{x=substr($4,3);y=$3x$NF;if(y==a[$3]){print $0}}' c1 c2
01 1111 AAA WW001 $$$$ 1000.00
02 2222 BBB GG001 %%%% 2000.00
04 4444 DDD FF002 &&&& 4000.00
05 5555 EEE RR002 @@@@ 5000.00
06 666 FFF UU003 JJJJ 6000.00
现将相同的取出来 a[$1]=AAA0011000.00 等等 然后比对相同的 先设置变量 将第二个文件的$4的最后三个字符取出来,然后在设置变量y= 设置相同的字段
要判断是否相同 用if 因为a[aaa] 在第二个文件的第三段所以是a[$3]
cat d1
redis:x:497:495::/home/redis:/bin/bash
gogs:x:1100:1100::/home/gogs:/bin/bash
abc:x:1101:1101::/home/abc:/bin/bash
bbb:x:1102:1102::/home/bbb:/bin/bash
apache:x:48:48:Apache:/var/www:/sbin/nologin
cat d2
redis:888888:16631::::::
gogs:888888:16633:0:99999:7:::
abc:888888:16661:0:99999:7:::
bbb:888888:16661:0:99999:7:::
apache:888888:16707::::::
awk -F':' 'NR==FNR{a[$1]=$2}NR!=FNR{$2=a[$1];print $0}' d2 d1
redis 888888 497 495 /home/redis /bin/bash
gogs 888888 1100 1100 /home/gogs /bin/bash
abc 888888 1101 1101 /home/abc /bin/bash
bbb 888888 1102 1102 /home/bbb /bin/bash
apache 888888 48 48 Apache /var/www /sbin/nologin
找相同的地方设置数组 发现$1相同 然后下标为要替换的数值 在第一个文件里取出来要替换的数值
然后在第二个文件里将 $2重新赋值 打印赋值之后的行即可
发现输出之后的分隔符还是有问题重新定义OFS
awk 'BEGIN{FS=OFS=":"}NR==FNR{a[$1]=$2}NR!=FNR{$2=a[$1];print $0}' d2 d1
redis:888888:497:495::/home/redis:/bin/bash
gogs:888888:1100:1100::/home/gogs:/bin/bash
abc:888888:1101:1101::/home/abc:/bin/bash
bbb:888888:1102:1102::/home/bbb:/bin/bash
apache:888888:48:48:Apache:/var/www:/sbin/nologin
==========================================================================
awk 使用实例
统计重复连接最多的ip
netstat -nat|grep ESTABLISHED | awk '{print $5}'|awk -F ':' '{print $1}' | sort | uniq -c | sort -rn | head -n 20
统计tcp连接状态的个数
netstat -nat | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
awk截时间内的取日志
awk '$3 ~ /11/Aug/2015:12/,/11/Aug/2015:13/' www.xxxxxx.com_access.log