Linux文本处理三剑客之一——awk详解——awk看这两篇就够啦~PS:文末有练习,来练练手吧

Posted xiaoxie_coding

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux文本处理三剑客之一——awk详解——awk看这两篇就够啦~PS:文末有练习,来练练手吧相关的知识,希望对你有一定的参考价值。

目录

awk流程控制语句

1.条件判断语句

2.循环语句

3.其他语句

awk数组

数组语法格式

for循环读取数组的值

数组相关函数

多维数组

数组的相关应用

awk内置函数

练习


awk流程控制语句

1.条件判断语句

单分支

# if(condition) statement1 
awk -F: 'if($3>500)print $1;$7' /etc/passwd
# if(condition) statement1 ; else statement2
awk -F: 'if($3>500)print $1;else print$3' /etc/passwd

 awk分支结构允许嵌套,大致格式如下:

 每条命令语句后面使用分号;结尾

awk -F: 'if($1=="root")print $1;else if($1=="ftp")print $2;else if($1=="mail")print $3;else print NR' /etc/passwd

cat /etc/passwd |awk -F: 'BEGINnum1=0;num2=0 if (length($2)<=2) print $1;num1++ else num2++;print $1ENDprint "有"num1"个用户没有设置密码,有"num2"个用户设置了密码"'

2.循环语句

while语句

示例如下:

awk 'BEGIN
total=100;x=0;
while(i<=total)
  x+=i;
  i++;
     
print x;
'
5050

 for循环

 代码示例如下:

# 定义数组,for循环取出数组里的值
awk -F: 'BEGINa[0]a[$3]=$1ENDfor (i in a) print a[i] ' /etc/passwd
# 利用for的条件循环
awk 'BEGIN  for (i = 1; i <= 10; i++) print i '

 do...while语句

代码示例如下

awk 'BEGIN                                                             
total=0;i=0;
do total+=i;i++; while(i<=100)
  print total;
'
5050

3.其他语句

  • break 一般用于 while 或 for 语句,退出程序循环。
  • continue  continue 语句用于 while 或 for 语句,在循环体内部结束本次循环,从而直接进入下一次循环迭代。
  • exit
[root@kafka01 ~]# cat /etc/passwd|awk -F: 'print $1;print$2;exit;print $3'
root
x
[root@kafka01 ~]# cat /etc/passwd|awk -F: 'BEGINprint "start";exit;NR==1print $0ENDprint "end"'
start
end

因为跳出了awk命令,所以后面的 print 语句不再执行.当 awk 语句中有 “exit” 和 “ENDcommands 语句块” 的时候,exit 并不是表示退出awk命令;而是表示直接执行 ENDcommands 语句块中的内容。

  • next 读取下一条记录;awk循环逐行匹配时,如果遇到next,就会跳过当前行,直接忽略下面语句进行下一行匹配。如果我们有一行内容不需要 awk 处理,那么我们就可以使用 “next” 来告诉 awk 哪一行内容不需要处理

# 当行号除以2余1,就跳过当前行。
[root@kafka01 1-6awk]# cat url.txt |awk 'NR%2==1nextprint NR,$0'
2 http://www.sina.com
4 http://www.12306.cn
6 feng@qq.com
8 meng.xianhui@yahoo.cn
10 10001@qq.com

awk数组

首先我们介绍一下数组

数组语法格式

array_name[index]=value
  • array_name:数组的名称
  • index:数组索引
  • value:数组中元素所赋予的值

数字和字符串都可以做数组的索引(下标)

for循环读取数组的值

awk -F: 'BEGINa[0]a[$3]=$1ENDfor (i in a) print a[i] ' /etc/passwd

BEGINa[0]定义一个数组a, a[0]是数组初始化的操作

a[$3]=$1把uid做key,用户名做value
for (i in a) print a[i] 使用for循环取出a数组里的key,然后输出对应的value, i对应a数组里的key


for…in循环输出时候,默认打印出来是无序数组

cat number.txt
81 90
62 30
43 85
34 53
15 77
# 默认打印出来的是无序数组
awk 'a[$1]=$2ENDfor (i in a) print i,a[i]' number.txt 
15 77
34 53
43 85
62 30
81 90

数组相关函数

1.length 统计数组的长度

awk -F: 'BEGINa[0]a[$3]=$1ENDfor (i in a) print a[i];print length(a) ' /etc/passwd

length返回字符串以及数组长度,split分割字符串info,动态创建数组A,返回值也是数组长度。

awk 'BEGINinfo="it is a test";lens=split(info,A," ");print length(A),lens,A[2];'
4 4 is

2. 排序函数

asort 对 value 排序,返回数组元素的个数,排序后,数组下标改为从1到数组的长度。
asorti 对 key 排序,返回数组元素的个数。
对上面的例子,我们对它分别使用asort和asorti排序:

asort排序

awk 'a[$1]=$2ENDt=asort(a,b);for (i=1;i<=t;i++) print i"\\t"a[i]"\\t"b[i]' number.txt 
1		30
2		53
3		77
4		85
5		90

END语句中 asort 对数组a的值进行排序,把排序后的下标存入新生成的数组b中,丢弃数组a下标值,再返回数组a的长度,将其赋值给变量t。
b[1]=30  
b[2]=53
b[3]=77
b[4]=85
b[5]=90         
a 数组原封未动,b数组首先拷贝自 a 数组,然后在 b 数组上排序。
例子中无法输出 a 数组的原因是,a 的索引为与传入的 1,2,3,4,5 不匹配,故没有内容输出


数组的值是无法直接输出的,如果想要得到排序的结果,则必需使用 for(i=1;i<length;i++) 的形式,因为 for(i in a) 的形式不能有序输出结果。


asorti排序

awk 'a[$1]=$2ENDt=asorti(a,b);for (i=1;i<=t;i++) print i,b[i],a[b[i]]' number.txt 
1 15 77
2 34 53
3 43 85
4 62 30
5 81 90

b[1]=15 
b[2]=34 
b[3]=43 
b[4]=62 
b[5]=81            b[i]中存的是索引值
END中 asorti 对数组a的下标进行排序,并把排序后的下标存入新生成的数组b中,并把数组a的长度赋值给变量t。asorti(a,b),得到排序后的索引,再将其代入未改变的 a,即a[b[i]],则可以得到索引对应的值。


多维数组

awk在存储上并不支持多维数组,不过我们可以很容易地使用一维数组模拟实现多维数组,awk提供了逻辑上模拟二维数组的方式,比如,array[0,0]这样的方式是允许的

如下示例为一个 3x3 的二维数组:

100 200 300
400 500 600
700 800 900

以上实例中,array[0][0] 存储 100,array[0][1] 存储 200 ,依次类推。为了在 array[0][0] 处存储 100, 我们可以使用如下语法: array["0,0"] = 100。

我们使用了 0,0 作为索引,但是这并不是两个索引值,而是一个字符串索引 0,0

# 定义多维数组
awk 'BEGIN 
array["0,0"] = 100;
array["0,1"] = 200;
array["0,2"] = 300;
array["1,0"] = 400;
array["1,1"] = 500;
array["1,2"] = 600;
# 输出数组元素
print "array[0,0] = " array["0,0"];
print "array[0,1] = " array["0,1"];
print "array[0,2] = " array["0,2"];
print "array[1,0] = " array["1,0"];
print "array[1,1] = " array["1,1"];
print "array[1,2] = " array["1,2"];
'

数组的相关应用

1.某一列有重复的值,而对应的另一列内容不一样,想实现一个累加功能,如:计算以下每个学生的总分(chinese+math的成绩),输出以下格式

cat student.txt 
name	course	grade
rose	chinese	80
jack	math 	90
rose	math 	89
jack    chinese	99
tom     chinese 60
tom     math    83
jons	math    82
# 输出以下格式(名字输出顺序随意)
rose  169
jack  189
tom   143
jons  82

# 答案
cat student.txt | awk 'NR>1name[$1]+=$3ENDfor (i in name) print i,name[i]' 

2.多维数组实现九九乘法

awk 'BEGIN
for(i=1;i<=9;i++)
  for(j=1;j<=9;j++)
    x[i,j]=i*j; print i,"*",j,"=",x[i,j];
  

'

awk内置函数

挑几个自己常用的来写一下,具体大家可以 man awk 有许多内置函数

1.length 计算字符数目的函数 可以直接计算出字符串的数目很方便

# 检查有无空口令用户
awk -F: 'length($2)<3 print $1' /etc/shadow
# 上次我们说过密码字段为*、!!表示没有设置密码

# 显示文件中超过60个字符的行  
awk 'length($0)>60 print NR,$0' /etc/passwd 

2.system函数 执行shell命令

批量创建和删除文件、用户等很方便

[root@kafka01 ~]# cat list
xixi 123
haha 456
hehe 789
[root@kafka01 ~]# awk 'system("useradd "$1)' list
[root@kafka01 ~]# ls /home
haha  hehe  hello  xixi
[root@kafka01 ~]# awk 'system("userdel -r "$1)' list 
[root@kafka01 ~]# ls /home
hello
# 思考如何利用system函数给上述用户配置密码

3.substr 截取字符串 可以指定截取位置的字符串

# 切片第一个字母到第三个
cat /etc/passwd|awk -F: 'print substr($1,1,3)' 

awk还内置了算数函数、字符串函数和时间函数等,还有很多有意思的函数等你去探索!

练习

1.求出3个人累计消费的金额,按照金额的大小排降序

[root@kafka01 0426test]# cat money.txt 
username    money
feng    100
feng    200
feng    350
li    200
li    239
li    890
zhang  100
zhang   350

# 答案
[root@kafka01 0426test]# awk 'NR>1 a[$1]+=$2ENDfor (i in a) print i,a[i]' money.txt |sort -k 2 
li 1329
feng 650
zhang 450

2.利用awk的if多分支判断用户的类型,uid为0显示为"管理员",如果是uid在1~999之间,为"程序用户",uid大于等于1000,为'普通用户",并统计他们的数量,最后输出时,格式如下:

xxx 是 xxx;xxx 是 xxx;xxx 是 xxx.....

管理员有x个;程序用户有x个;普通用户有x个;总共有x个用户 或输出

管理员有:xxx,... 数量有:  ,程序用户有:xxx... 数量有:  ,普通用户有:xxx... 数量有:

cat /etc/passwd|awk -F: 'BEGINa=0;b=0;c=0if ($3==0)print $1"是管理员";a++else if ($3>1 && $3<=999)print $1"是程序用户";b++else print $1"是普通用户";c++ENDprint "总共有",a+b+c,"个用户"'  

用数组的方式来写:

# 利用数组
# 将三种类别分为三个组 value存储的是三种类别的用户名 然后利用字符串拼接起来
awk -F: 'BEGINsuper=0;admin=0;user=0;admin1="";user1=""
if($3==0)super=super+1;A[super]=$1
else if($3>0&&$3<=999)admin=admin+1;B[admin]=$1
else user=user+1;C[user]=$1
END
for (k in A)print "管理员有",A[k],"数量是",super;
for (k in B)admin1=admin1 " " B[k];print "程序用户有",admin1,"数量是",admin;
for (k in C)user1=user1 " " C[k];print "普通用户有",user1,"数量是",user;
print "总共有",admin+super+user,"个用户"' /etc/passwd

直接用字符串拼接不使用数组

awk -F: 'BEGINsuper=0;admin=0;user=0;a="";b="";c=""

if ($3==0)super++;a=a" "$1 
else if($3>0 && $3<1000)admin++;b=b" "$1 
else user++;c=c" "$1;

ENDprint "管理员有:",a,"数量为:",super,
"\\n程序用户有:",b,"数量为:",admin,
"\\n普通用户有:",c,"数量为:",user,
"\\n总共有",super+admin+user"个用户"' /etc/passwd

3.利用awk的system命令在/tmp下建立和删除/etc/passwd中与用户名同名的目录

awk -F: 'system("mkdir /tmp/"$1)' /etc/passwd 
awk -F: 'system("rm -rf /tmp/"$1)' /etc/passwd 

4.awk进行行求和

echo 1 2 3 4 5|awk 'for (i=1;i<=NF;i++) sum+=$i;print sum'
15
seq -s ' ' 100 | awk 'for(i=1;i<=NF;i++) sum+=$i; print sum'
5050

其他类似于:统计出ftp下载量最大的5个ip地址累计下载的总大小(以M为单位);进行下载流量排序;统计每个URL的每分钟的频率等常见面试题其实都是要用到数组的,要熟练使用

awk系列就到这里了,经过这两天的学习我了解到awk是一个很强大的命令,我们所知道的只是它的一小部分,但单单一小部分就够我们使用了,其实Linux中每一个命令都很强大,都值得我们去认真学习!所以,一起加油吧!

PS:文章要有写的不太对的地方欢迎评论私信我一起交流呀!

以上是关于Linux文本处理三剑客之一——awk详解——awk看这两篇就够啦~PS:文末有练习,来练练手吧的主要内容,如果未能解决你的问题,请参考以下文章

Linux文本三剑客之一——awk详解——awk看这两篇就够啦~PS:文末有练习,来练练手吧

     awk (三剑客之一)

Linux三剑客值awk命令详解

linux文本处理三剑客——awk

Linux上文本三剑客之awk详解

Linux三剑客之一awk