整理的几个好一点的awk案例

一,有这么两个文本,要求以文本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}!($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(!($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


案例二,

需求:

nginxaccess.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匹配失败,因此不输出这条记录,后续的数据以此类推,最终成功实现去除文件中的重复行。



本文转自 aklaus 51CTO博客,原文链接:http://blog.51cto.com/aklaus/1844797

上一篇:Linux之文件安全上下文及特殊权限位


下一篇:windows和linux中检查端口是否被占用