一,有这么两个文本,要求以文本a的第一个字段为比较,如果文本b的第一个字段与a不符合,那么打印出该行。
$ cat a
1 a
2 f
3 5
4 8
$ cat b
1 a
2 f
3 5
5 8
这里用到FNR(记录当前处理文件的行数) NR(记录当前处理的总行数) next(处理下一条记录,不行后边的action) awk默认执行的action print $0
awk 'NR==FNR{a[$1];next}!($1 in a)' a b
首先NR==FNR对文本a进行处理,把$1的值作为下标放入数组a,next不会执行后面的语句,一直读到文本b不满足NR==FNR条件,这时判断文 本b的$1是否存在于数组a中的下标中,显然文本b的第一行的$1是存在于数组a的下标中的,那么条件为真,再!,注意非真即为假,为假那么不会执行该 pattern后面默认的{print]这个action动作,那么则不会输出该行,一直到第4行的$1并未存在于数组a的下标中,那么条件为假,非假为 真,则执行默认的打印,输出了该行。或许下面的写法更符合规范和便于理解:
awk 'NR==FNR{a[$1];next}{if(!($1 in a))print $0}' a b
1)将d文件性别合并到c文件
$ cat c
zhangsan 100
lisi 200
wangwu 300
$ cat d
zhangsan man
lisi woman
方法1:$ awk 'FNR==NR{a[$1]=$0;next}{if($1 in a) print a[$1],$2}' c d
2)找不同记录(同上,取反)
$ awk 'FNR==NR{a[$0];next}!($0 in a)' a b
$ awk 'FNR==NR{a[$0]=1;next}!a[$0]' a b
$ awk 'ARGIND==1{a[$0]=1}ARGIND==2&&a[$0]!=1' a b
$ awk 'FILENAME=="a"{a[$0]=1}FILENAME=="b"&&a[$0]!=1' a b
7
8
方法2:$ sort a b |uniq -d
方法3:$ grep -vf a b
3、合并两个文件
1)将d文件性别合并到c文件
$ cat c
zhangsan 100
lisi 200
wangwu 300
$ cat d
zhangsan man
lisi woman
方法1:$ awk 'FNR==NR{a[$1]=$0;next}{print a[$1],$2}' c d
zhangsan 100 man
lisi 200 woman
wangwu 300 man
方法2:$ awk 'FNR==NR{a[$1]=$0}NR>FNR{print a[$1],$2}' c d
说明:NR==FNR匹配第一个文件,NR>FNR匹配第二个文件,将$1为数组下标
方法3:$ awk 'ARGIND==1{a[$1]=$0}ARGIND==2{print a[$1],$2}' c d
2)将a.txt文件中服务名称合并到一个IP中
$ cat a.txt
192.168.2.100 : httpd
192.168.2.100 : tomcat
192.168.2.101 : httpd
192.168.2.101 : postfix
192.168.2.102 : mysqld
192.168.2.102 : httpd
$ awk -F: -vOFS=":" '{a[$1]=a[$1] $2}END{for(i in a)print i,a[i]}' a.txt
$ awk -F: -vOFS=":" '{a[$1]=$2 a[$1]}END{for(i in a)print i,a[i]}' a.txt
192.168.2.100 : httpd tomcat
192.168.2.101 : httpd postfix
192.168.2.102 : mysqld httpd
说明:a[$1]=$2 第一列为下标,第二个列是元素,后面跟的a[$1]是通过第一列取a数组元素(服务名),结果是$1=$2 $2,并作为a数组元素。
3)将第一行附加给下面每行开头
$ cat a.txt
xiaoli
a 100
b 110
c 120
$ awk 'NF==1{a=$0;next}{print a,$0}' a.txt
$ awk 'NF==1{a=$0}NF!=1{print a,$0}' a.txt
xiaoli a 100
xiaoli b 110
xiaoli c 120
4、倒叙列打印文本
$ cat a.txt
xiaoli a 100
xiaoli b 110
xiaoli c 120
$ awk '{for(i=NF;i>=1;i--){printf "%s ",$i}print s}' a.txt
100 a xiaoli
110 b xiaoli
120 c xiaoli
$ awk '{for(i=NF;i>=1;i--)if(i==1)printf $i"\n";else printf $i" "}' a.txt
说明:利用NF降序输出,把最后一个域作为第一个输出,然后自减,print s或print ""打印一个换行符
5、从第二列打印到最后
方法1:$ awk '{for(i=2;i<=NF;i++)if(i==NF)printf $i"\n";else printf $i" "}' a.txt
方法2:$ awk '{$1=""}{print $0}' a.txt
a 100
b 110
c 120
6、将c文件中第一列放到到d文件中的第三列
$ cat c
a
b
c
$ cat d
1 one
2 two
3 three
方法1:$ awk 'FNR==NR{a[NR]=$0;next}{$3=a[FNR]}1' c d
说明:以NR编号为下标,元素是每行,当处理d文件时第三列等于获取a数据FNR(重新计数1-3)编号作为下标。
方法2:$ awk '{getline f<"c";print $0,f}' d
1 one a
2 two b
3 three c
1)替换第二列
$ awk '{getline f<"c";gsub($2,f,$2)}1' d
1 a
2 b
3 c
2)替换第二列的two
$ awk '{getline f<"c";gsub("two",f,$2)}1' d
1 one
2 b
3 three
7、数字求和
方法1:$ seq 1 100 |awk '{sum+=$0}END{print sum}'
方法2:$ awk 'BEGIN{sum=0;i=1;while(i<=100){sum+=i;i++}print sum}'
方法3:$ awk 'BEGIN{for(i=1;i<=100;i++)sum+=i}END{print sum}' /dev/null
方法4:$ seq -s + 1 100 |bc
8、每隔三行添加一个换行符或内容
方法1:$ awk '$0;NR%3==0{printf "\n"}' a
方法2:$ awk '{print NR%3?$0:$0"\n"}' a
方法3:$ sed '4~3s/^/\n/' a
9、字符串拆分
方法1:
$ echo "hello" |awk -F '' '{for(i=1;i<=NF;i++)print $i}'
$ echo "hello" |awk -F '' '{i=1;while(i<=NF){print $i;i++}}'
h
e
l
l
o
方法2:
$ echo "hello" |awk '{split($0,a,"''");for(i in a)print a[i]}' #无序
l
o
h
e
l
10、统计字符串中每个字母出现的次数
$ echo a,b.c.a,b.a |tr "[,. ]" "\n" |awk -F '' '{for(i=1;i<=NF;i++)a[$i]++}END{for(i in a)print i,a[i]|"sort -k2 -rn"}'
a 3
b 2
c 1
11、第一列排序
$ awk '{a[NR]=$1}END{s=asort(a,b);for(i=1;i<=s;i++){print i,b[i]}}' a.txt
说明:以每行编号作为下标值为$1,并将a数组值放到数组b,a下标丢弃,并将asort默认返回值(原a数组长度)赋值给s,使用for循环小于s的行号,从1开始到数组长度打印排序好的数组。
12、删除重复行,顺序不变
$ awk '!a[$0]++' file
博客地址: http://lizhenliang.blog.51cto.com
13、删除指定行
删除第一行:
$ awk 'NR==1{next}{print $0}' file #$0可省略
$ awk 'NR!=1{print}' file
$ sed '1d' file
$ sed -n '1!p' file
14、在指定行前后加一行
在第二行前一行加txt:
$ awk 'NR==2{sub('/.*/',"txt\n&")}{print}' a.txt
$ sed'2s/.*/txt\n&/' a.txt
在第二行后一行加txt:
$ awk 'NR==2{sub('/.*/',"&\ntxt")}{print}' a.txt
$ sed'2s/.*/&\ntxt/' a.txt
15、通过IP获取网卡名
$ ifconfig |awk -F'[: ]' '/^eth/{nic=$1}/192.168.18.15/{print nic}'
16、浮点数运算(数字46保留小数点)
$ awk 'BEGIN{print 46/100}'
$ awk 'BEGIN{printf "%.2f\n",46/100}'
$ echo 46|awk '{print $0/100}'
$ echo 'scale=2;46/100' |bc|sed 's/^/0/'
$ printf "%.2f\n" $(echo "scale=2;46/100" |bc)
结果:0.46
17、替换换行符为逗号
$ cat a.txt
1
2
3
替换后:1,2,3
方法1:
$ awk '{s=(s?s","$0:$0)}END{print s}' a.txt
说明:三目运算符(a?b:c),第一个s是变量,s?s","$0:$0,第一次处理1时,s变量没有赋值初值是0,0为假,结果打印1,第二次处理2时,s值是1,为真,结果1,2。以此类推,小括号可以不写。
方法2:
$ tr '\n' ',' < a.txt
案例二,
需求:
对nginx的access.log日志分析,要求最近100次请求中状态值不是200的百分比。
日志格式如下:
192.168.123.6 - - [26/May/2015:23:44:21+0800] "GET /get_seller_info&format=json HTTP/1.1" 200 34679"-" "Dalvik/1.6.0 (Linux; U; Android 4.4.4; MX4 ProBuild/KTU84P)" "-" "3.562"
awk '{if($8 ~ /^[0-9]/)print $8}' filename.log | tail -100 | awk '{if($1!=200) s++;}END{print s*100/NR"%"}'
解释:首先按默认分组取状态值,然后取最近100次,然后进行打印,都是基础知识点。
1)统计日志中访问最多的10个IP
思路:对第一列进行去重,并输出出现的次数
方法1:$ awk '{a[$1]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log
方法2:$ awk '{print $1}' access.log |sort |uniq -c |sort -k1 -nr |head -n10
说明:a[$1]++ 创建数组a,以第一列作为下标,使用运算符++作为数组元素,元素初始值为0。处理一个IP时,下标是IP,元素加1,处理第二个IP时,下标是IP,元素加1,如果这个IP已经存在,则元素再加1,也就是这个IP出现了两次,元素结果是2,以此类推。因此可以实现去重,统计出现次数。
2)统计日志中访问大于100次的IP
方法1:$ awk '{a[$1]++}END{for(i in a){if(a[i]>100)print i,a[i]}}' access.log
方法2:$ awk '{a[$1]++;if(a[$1]>100){b[$1]++}}END{for(i in b){print i,a[i]}}' access.log
说明:方法1是将结果保存a数组后,输出时判断符合要求的IP。方法2是将结果保存a数组时,并判断符合要求的IP放到b数组,最后打印b数组的IP。
3)统计2016年4月9日一天内访问最多的10个IP
思路:先过滤出这个时间段的日志,然后去重,统计出现次数
方法1:$ awk '$4>="[9/Apr/2016:00:00:01" && $4<="[9/Apr/2016:23:59:59" {a[$1]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log
方法2:$ sed -n '/\[9\/Apr\/2016:00:00:01/,/\[9\/Apr\/2016:23:59:59/p' access.log |sort |uniq -c |sort -k1 -nr |head -n10 #前提开始时间与结束时间日志中必须存在
4)统计当前时间前一分钟的访问数
思路:先获取当前时间前一分钟对应日志格式的时间,再匹配统计
$ date=$(date -d '-1 minute' +%d/%b/%Y:%H:%M);awk -vdate=$date '$0~date{c++}END{print c}' access.log
$ date=$(date -d '-1 minute' +%d/%b/%Y:%H:%M);awk -vdate=$date '$4>="["date":00" && $4<="["date":59"{c++}END{print c}' access.log
$ grep -c $(date -d '-1 minute' +%d/%b/%Y:%H:%M) access.log
说明:date +%d/%b/%Y:%H:%M --> 09/Apr/2016:01:55
5)统计访问最多的前10个页面($request)
$ awk '{a[$7]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log
6)统计每个URL访问内容的总大小($body_bytes_sent)
$ awk '{a[$7]++;size[$7]+=$10}END{for(i in a)print a[i],size[i],i}' access.log
7)统计每个IP访问状态码数量($status)
$ awk '{a[$1" "$9]++}END{for(i in a)print i,a[i]}' access.log
8)统计访问状态码为404的IP及出现次数
$ awk '{if($9~/404/)a[$1" "$9]++}END{for(i in a)print i,a[i]}' access.log
参考:http://www.tuicool.com/articles/JFnaIrY
重复的数据总是让人各种不爽,占用空间、看起来费劲等等,今天就介绍一个通过awk去除文件中重复数据的办法,awk默认是一行行来处理数据的,那我们就重点说说如何通过awk去除文件中的重复行。
首先准备一个文本文件,随便写个文件,包含重复行数据的即可,或者你可以参考我这里的文件:
CodingAnts@ubuntu:~/awk$ cat dup
hello world
awk
coding ants
hello world
awk
hello world
awk
coding ants
coding ants
共有9行,后面6行都是重复的前面的几行,最终的效果应该是只显示上面重点显示的那几行,先来看看效果:
CodingAnts@ubuntu:~/awk$ awk '!a[$0]++' dup hello world awk coding ants
在《awk程序指令模型》中介绍了awk的程序指令由模式和操作组成,即Pattern { Action }的形式,如果省略Action,则默认执行 print $0 的操作。
实现去除重复功能的就是这里的Pattern:
!a[$0]++
在awk中,对于未初始化的数组变量,在进行数值运算的时候,会赋予初值0,因此a[$0]=0,++运算符的特性是先取值,后加1,因此Pattern等价于
!0
而0为假,!为取反,因此整个Pattern最后的结果为1,相当于if(1),Pattern匹配成功,输出当前记录,对于dup文件,前3条记录的处理方式都是如此。
当读取第4行数据“hello world”的时候,a[$0]=1,取反后的结果为0,即Pattern为0,Pattern匹配失败,因此不输出这条记录,后续的数据以此类推,最终成功实现去除文件中的重复行。