Linux 三剑客之 awk(3):awk 数组与语法

阅读数:1 2020 年 2 月 10 日 21:06

Linux三剑客之awk(3):awk数组与语法

一、awk 数组

1.1 数组结构

图片 5.png

people[police]=110

people[doctor]=120

[root@creditease awk]# awk 'BEGIN{word[0]="credit";word[1]="easy";print word[0],word[1]}'  
credit easy  
[root@creditease awk]# awk 'BEGIN{word[0]="credit";word[1]="easy";for(i in word)print word[i]}'  
credit  
easy  

1.2 数组分类

索 引数组:以数字为下标
关联数组:以字符串为下标

1.3 awk 关联数组

现有如下文本,格式如下:即左边是随机字母,右边是随机数字, 即将相同的字母后面的数字加在一起,按字母的顺序输出

a  1  
b  3  
c  2  
d  7  
b  5  
a  3   
g  2  
f  6  

以 $1 为下标,创建数组 a[$1]=a[$1]+$2(a[$1]+=$2)然后配合 END 和 for 循环输出结果:

[root@creditease awk]# awk '{a[$1]=a[$1]+$2}END{for(i in a)print i,a[i]}' jia.txt   
a 4  
b 8  
c 2  
d 7  
f 6  
g 2  
注意:for(i in a) 循环的顺序不是按照文本内容的顺序来处理的,排序可以在命令后加 sort 排序  

1.4 awk 索引数组

以数字为下标的数组 seq 生成 1-10 的数字,要求只显示计数行

[root@creditease awk]# seq 10|awk '{a[NR]=$0}END{for(i=1;i<=NR;i+=2){print a[i]}}'  
1  
3  
5  
7  
9  

seq 生成 1-10 的数字,要求不显示文件的后 3 行

[root@creditease awk]# seq 10|awk '{a[NR]=$0}END{for(i=1;i<=NR-3;i++){print a[i]}}'  
1  
2  
3  
4  
5  
6  
7  
解析:改变 i 的范围即可,多用于不显示文件的后几行  

1.5 awk 数组实战去重

a++ 和 ++a

[root@creditease awk]# awk 'BEGIN{print a++}'  
0  
[root@creditease awk]# awk 'BEGIN{print ++a}'  
1  
[root@creditease awk]# awk 'BEGIN{a=1;b=a++;print a,b}'  
2 1  
[root@creditease awk]# awk 'BEGIN{a=1;b=++a;print a,b}'  
2 2  

注:  

都是 b = a+1  

b=a++ 先把 a 的值赋予 b,然后 a + 1  

b=++a 先执行 a+1, 然后把 a 的值赋予 b  

对一下文本进行去重处理 针对第二列去重

[root@creditease awk]# cat qc.txt   
2018/10/20   xiaoli     13373305025  
2018/10/25   xiaowang   17712215986  
2018/11/01   xiaoliu    18615517895   
2018/11/12   xiaoli     13373305025  
2018/11/19   xiaozhao   15512013263  
2018/11/26   xiaoliu    18615517895  
2018/12/01   xiaoma     16965564525  
2018/12/09   xiaowang   17712215986  
2018/11/24   xiaozhao   15512013263  
解法一:  
[root@creditease awk]# awk '!a[$2]++' qc.txt   
2018/10/20   xiaoli     13373305025  
2018/10/25   xiaowang   17712215986  
2018/11/01   xiaoliu    18615517895   
2018/11/19   xiaozhao   15512013263  
2018/12/01   xiaoma     16965564525  
解析:  
!a[$3]++ 是模式(条件),命令也可写成 awk '!  
a[$3]=a[$3]+1{print $0}' qc.txt  
a[$3]++ ,“++”在后,先取值后加一  
!a[$3]=a[$3]+1:是先取 a[$3] 的值,比较“!a[$3]”是否符合条件 (条件非 0),后加 1  
注意:此方法去重后的结果显示的是文本开头开始的所有不重复的行  
解法二:  
[root@creditease awk]# awk '++a[$2]==1' qc.txt   
2018/10/20   xiaoli     13373305025  
2018/10/25   xiaowang   17712215986  
2018/11/01   xiaoliu    18615517895   
2018/11/19   xiaozhao   15512013263  
2018/12/01   xiaoma     16965564525  
解析:  
++a[$3]==1 是模式(条件),也可写成 a[$3]=a[$3]+1==1 即只有当条件(a[$3]+1 的结果)为 1 的时候才打印出内容  
++a[$3] ,“++”在前,先加一后取值  
++a[$3]==1:是先加 1,后取 a[$3] 的值,比较“++a[$3]”是否符合条件(值为 1)  
注意:此方法去重后的结果显示的是文本开头开始的所有不重复的行  
解法三:  
[root@creditease awk]# awk '{a[$2]=$0}END{for(i in a){print a[i]}}' qc.txt  
2018/11/12   xiaoli     13373305025  
2018/11/26   xiaoliu    18615517895  
2018/12/01   xiaoma     16965564525  
2018/12/09   xiaowang   17712215986  
2018/11/24   xiaozhao   15512013263  

解析:  
注意此方法去重后的结果显示的是文本结尾开始的所有不重复的行  

1.6 awk 处理多个文件(数组、NR、FNR)

使用 awk 取 file.txt 的第一列和 file1.txt 的第二列然后重定向到一个新文件 new.txt 中

[root@creditease awk]# cat file1.txt   
a b  
c d  
e f  
g h  
i j  
[root@creditease awk]# cat file2.txt   
1 2  
3 4  
5 6  
7 8  
9 10  
[root@creditease awk]# awk 'NR==FNR{a[FNR]=$1}NR!=FNR{print a[FNR],$2}' file1.txt file2.txt   
a 2  
c 4  
e 6  
g 8  
i 10  
解析:NR==FNR 处理的是第一个文件,NR!=FNR 处理的是第二个文件.  
注意:当两个文件 NR(行数) 不同的时候,需要把行数多的放前边.  
解决方法:把行数多的文件放前边,行数少的文件放后边.  
把输出的结果放入一个新文件 new.txt 中:  
[root@creditease awk]# awk 'NR==FNR{a[FNR]=$1}NR!=FNR{print a[FNR],$2>"new.txt"}' file1.txt file2.txt   
[root@creditease awk]# cat new.txt   
a 2  
c 4  
e 6  
g 8  
i 10  

1.7 awk 分析日志文件,统计访问网站的个数

[root@creditease awk]# cat url.txt   
http://www.baidu.com  
http://mp4.video.cn  
http://www.qq.com  
http://www.listeneasy.com  
http://mp3.music.com  
http://www.qq.com  
http://www.qq.com  
http://www.listeneasy.com  
http://www.listeneasy.com  
http://mp4.video.cn  
http://mp3.music.com  
http://www.baidu.com  
http://www.baidu.com  
http://www.baidu.com  
http://www.baidu.com  
[root@creditease awk]# awk -F "[/]+" '{h[$2]++}END{for(i in h) print i,h[i]}' url.txt  
www.qq.com 3  
www.baidu.com 5  
mp4.video.cn 2  
mp3.music.com 2  
www.crediteasy.com 3  

二、awk 简单语法

2.1 函数 sub gsub

替换功能

格式:sub(r, s , 目标) gsub(r, s , 目标)

[root@creditease awk]# cat sub.txt   
ABC DEF AHI GKL$123  
BAC DEF AHI GKL$213  
CBA DEF GHI GKL$321  
[root@creditease awk]# awk '{sub(/A/,"a");print $0}' sub.txt   
aBC DEF AHI GKL$123  
BaC DEF AHI GKL$213  
CBa DEF GHI GKL$321  
[root@creditease awk]# awk '{gsub(/A/,"a");print $0}' sub.txt   
aBC DEF aHI GKL$123  
BaC DEF aHI GKL$213  
CBa DEF GHI GKL$321  
注:sub 只会替换行内匹配的第一次内容;相当于 sed ‘s###’  
    gsub 会替换行内匹配的所有内容;相当于 sed ‘s###g’  
[root@creditease awk]# awk '{sub(/A/,"a",$1);print $0}' sub.txt   
aBC DEF AHI GKL$123  
BaC DEF AHI GKL$213  
CBa DEF GHI GKL$321  

练习:

0001|20081223efskjfdj|EREADFASDLKJCV  
0002|20081208djfksdaa|JDKFJALSDJFsddf  
0003|20081208efskjfdj|EREADFASDLKJCV  
0004|20081211djfksdaa1234|JDKFJALSDJFsddf  
以'|'为分隔, 现要将第二个域字母前的数字去掉,其他地方都不变, 输出为:  
0001|efskjfdj|EREADFASDLKJCV  
0002|djfksdaa|JDKFJALSDJFsddf  
0003|efskjfdj|EREADFASDLKJCV  
0004|djfksdaa1234|JDKFJALSDJFsddf  

方法:  
awk -F '|'  'BEGIN{OFS="|"}{sub(/[0-9]+/,"",$2);print $0}' sub_hm.txt  
awk -F '|'  -v OFS="|" '{sub(/[0-9]+/,"",$2);print $0}' sub_hm.txt  

2.2 if 和 slse 的用法

内容:

AA

BC

AA

CB

CC

AA

结果:

AA YES

BC NO YES

AA YES

CB NO YES

CC NO YES

AA YES

1) [root@creditease awk]# awk '{if($0~/AA/){print $0" YES"}else{print $0" NO YES"}}' ifelse.txt   
AA YES  
BC NO YES  
AA YES  
CB NO YES  
CC NO YES  
AA YES  
解析:使用 if 和 else,if $0 匹配到 AA,则打印 $0 "YES",else 反之打印 $0 " NO YES"。  
2)[root@creditease awk]# awk '$0~/AA/{print $0" YES"}$0!~/AA/{print $0" NO YES"}' ifelse.txt   
AA YES  
BC NO YES  
AA YES  
CB NO YES  
CC NO YES  
AA YES  
解析:使用正则匹配,当 $0 匹配 AA 时,打印出 YES,反之,打印出“NO YES”  

2.3 next 用法

如上题,用 next 来实现

next :跳过它后边的所有代码

 [root@creditease awk]# awk '$0~/AA/{print $0" YES";next}{print $0" NO YES"}' ifelse.txt   
AA YES  
BC NO YES  
AA YES  
CB NO YES  
CC NO YES  
AA YES  
解析:  
{print $0" NO YES"}:此动作是默认执行的,当前边的 $0~/AA/ 匹配,就会执行{print $0" YES";next}  
因为 action 中有 next,所以会跳过后边的 action。  
如果符合 $0~/AA/ 则打印 YES ,遇到 next 后,后边的动作不执行;如果不符合 $0~/AA/,会执行 next 后边的动作;  
next 前边的(模式匹配),后边的就不执行,前边的不执行(模式不匹配),后边的就执行。  

2.4 printf 不换行输出以及 next 用法

printf :打印后不换行

如下文本,如果 Description:之后为空,将其后一行内容并入此行。

Packages: Hello-1  
Owner: me me me me  
Other: who care?  
Description:  
Hello world!  
Other2: don't care  
想要结果:  
Packages: Hello-1  
Owner: me me me me  
Other: who care?  
Description: Hello world!  
Origial-Owner: me me me me  
Other2: don't care  
1)[root@creditease awk]# awk '/^Desc.*:$/{printf $0}!/Desc.*:$/{print $0}' printf.txt   
Packages: Hello-1  
Owner: me me me me  
Other: who care?  
Description:Hello world!  
Other2: don't care  
解析:使用正则匹配,匹配到'/^Desc.*:$/,就使用 printf 打印(不换行),不匹配的打印出整行。  
2)使用 if 和 else 实现  
[root@creditease awk]# awk '{if(/Des.*:$/){printf $0}else{print $0}}' printf.txt   
Packages: Hello-1  
Owner: me me me me  
Other: who care?  
Description:Hello world!  
Other2: don't care  
3)使用 next 实现  
[root@creditease awk]# awk '/Desc.*:$/{printf $0;next}{print $0}' printf.txt   
Packages: Hello-1  
Owner: me me me me  
Other: who care?  
Description:Hello world!  
Other2: don't care  
注:可简写成 awk '/Desc.*:$/{printf $0;next}1'  
printf.txt  ## 1 是 pattern(模式),默认 action(动作)是{print $0}  

2.5 去重后计数按要求重定向到指定文件

文本如下,要求计算出每项重复的个数,然后把重复次数大于 2 的放入 gt2.txt 文件中,把重复次数小于等于 2 的放入 le2.txt 文件中

[root@creditease files]# cat qcjs.txt   
aaa  
bbb  
ccc  
aaa  
ddd  
bbb  
rrr  
ttt  
ccc  
eee  
ddd  
rrr  
bbb  
rrr  
bbb  
[root@creditease awk]# awk '{a[$1]++}END{for(i in a){if(a[i]>2){print i,a[i]>"gt2.txt"}else{print i,a[i]>"le2.txt"}}}' qcjs.txt   
[root@creditease awk]# cat gt2.txt   
rrr 3  
bbb 4  
[root@creditease awk]# cat le2.txt   
aaa 2  
ccc 2  
eee 1  
ttt 1  
ddd 2  
解析:{print }, 或括号中打印后可直接重定向到一个新文件,文件名用双引号引起来。如: {print $1 >"xin.txt"}  

三、awk 需注意事项

a)NR==FNR ##不能写成 NR=FNR(= 在 awk 中是赋值的意思)

b)NR!=FNR ##NR 不等于 FNR

c){a=1;a[NR]} 这样会报错:同一条命令中变量和数组名不能重复 d)printf 输出的时候不换行

e){print }, 或括号中打印后可直接重定向到一个新文件,文件名用双引号引起来。如: {print $1 >“xin.txt”}

f) 当模式(条件)是 0 的时候,后边的动作不执行,!0 的时候后边动作才执行。

本文转载自宜信技术学院网站。

原文链接: http://college.creditease.cn/detail/262

评论

发布