格式化文本数据抽取工具awk
在管理和维护Linux系统过程中,有时可能需要从一个具有一定格式的文本(格式化文本)中抽取数据,这时可以使用awk编辑器来完成这项任务。发明这个工具的作者是Aho、Weinberg和Kernighan,取三个人名的首字母而得名awk。
与sed相比,awk更擅长处理格式化文本。格式化文本一般使用某个特定的字符(称为域分隔符)将文本中不同的字段(称为域)隔开。例如用于保存用户信息的系统用户文件/etc/passwd,该文件使用冒号分别将用户名、密码、UID等字段分隔开。
一、awk命令基本格式
- 命令格式:
awk [-F] ‘command’ input-file
awk –f script input-file
与sed类似,awk也有两种调用方式:第一种是直接使用awk命令调用,选项F用于指定域分隔符。默认情况下awk使用的域分隔符是空格,如果要处理的文件input-file的域分隔符不是空格,应该使用F选项另行指定。第二种方法跟sed一样,先将要输入的选项模式和动作放入一个脚本文件中,然后使用选项f调用。
- 命令处理过程:
awk被调用后,首先读入第一行文本并按选项F指定的域分隔符将各个字段划开。以/etc/passwd其中一行为例:
root:x:0:0:root:/root:/bin/bash
处理这个文件时,应该使用选项F指定域分隔符为冒号“:”,划分完成后将这一行称为一条记录。一条记录中的各个字段按顺序称为域1,域2,域3……为方便对这些字段进行处理,使用标识符“$1”表示第一个字段,“$2”表示第二个字段…依次类推。如果要表示整条记录,应该使用标识符“$0”。
划分记录后,awk会按预定的模式和动作处理记录。处理完一条记录后,又会读取文本第二行重复上述动作。
- 模式和动作:
一个完整的awk命令由编辑语句和格式化文本组成。编辑语句由一个或多个模式和动作组成,格式化文本即用户需要处理的文本,可以来自文件,也可以来自命令输出。
与sed一样,模式用来指定动作执行的文本位置。在awk中,模式可以是条件语句、模式匹配、正则表达式等。如果没有指定模式,awk会对所有记录执行编辑语句。
动作一般放在模式后面的大括号{}内,一般是awk的内置函数,例如输出函数print等。
下面是一个输出文件/etc/passwd中所有用户名的示例:
[root@localhost test]# awk -F: '{print "username:" $1}' /etc/passwd
username:root
username:bin
username:daemon
username:adm
username:lp
...
上面这个示例命令使用选项F指定域分隔符为冒号,后面的命令中并没有指定模式,awk将默认匹配所有行。放在大括号内的动作使用了awk的内置函数print,先输出了一个字符串“username:”,然后使用标识符“$1”输出第一个字段。
- 文本头、尾表达式:
在awk中,有两个特殊的表达式BEGIN和END。BEGIN语句将在编辑语句开始执行之前运行,通常用来输出文本头信息。END语句则在所有编辑语句完成之后执行,一般用来输出结束信息和统计数据等。
使用示例文件students(见查找文本工具grep),制作一个输出学生学号和姓名,并输出文本头、文本尾的例子:
[root@localhost test]# awk 'BEGIN{print "Student ID name\n-----------------------"}{print $1"\t"$2}END{print "------------END-----------"}' students
Student ID name
-----------------------
2821020225 Liulu
2821020115 Liumin
2721020321 Xuli
2921020632 Xiayu
2721010409 Liwei
2921050313 Heli
2721030227 Wangtao
------------END-----------
二、正则表达式、元字符、运算符和关系运算符
在awk中,除了可以使用正则表达式和元字符外,awk还内置了算术运算符、操作符。
- 常见的算术运算符:
=(赋值)、+(加)、-(减)、-(负)、*(乘)、/(除)、前置++、后置++、前置--、后置--、?:(条件)、%(求模)、+=(复合加)、-=(复合减)、*=(复合乘)、/=(复合除)、%=(复合求模)、^(幂)、^=(复合幂)
- 常见的关系运算符和操作符:
>(大于)、>=(大于等于)、<(小于)、<=(小于等于)、!=(不等于)、==(等于)
~/pattern/(匹配pattern)、!~/pattern/(不匹配pattern)
- 逻辑运算符:
||(或)、&&(与)、!(非)
用法示例:
(1)结合正则表达式,输出所有2007年入学的学生信息:
[root@localhost test]# awk '/27210/{print $0}' students
2721020321 Xuli Jiangsu Luolei 12/25/92 76 81 85 79 321 80
2721010409 Liwei Sichuan tangwei 11/21/92 98 88 85 85 356 89
2721030227 Wangtao Yunnan Huli 03/21/93 87 76 69 88 320 80
(2)结合正则表达式,输出所有来自Sichuan的学生学号、姓名和总分:
[root@localhost test]# awk '/Sichuan/{print $1"\t"$2"\t"$10}' students
2821020225 Liulu 325
2721010409 Liwei 356
(3)可以指定某个域的取值精确匹配。例如要输出姓名为Liwei的学生信息:
[root@localhost test]# awk '$2=="Liwei"{print $0}' students
2721010409 Liwei Sichuan tangwei 11/21/92 98 88 85 85 356 89
(4)精确匹配时,可以使用“!=”表示不匹配。例如要输出不是来自Sichuan的学生信息:
[root@localhost test]# awk '$3!="Sichuan"{print $0}' students
2821020115 Liumin Henan lixia 05/14/94 78 65 59 78 280 70
2721020321 Xuli Jiangsu Luolei 12/25/92 76 81 85 79 321 80
2921020632 Xiayu Shanxi Hetao 03/26/93 78 86 92 78 334 84
2921050313 Heli Xizang Tangwei 07/12/94 56 78 80 45 259 65
2721030227 Wangtao Yunnan Huli 03/21/93 87 76 69 88 320 80
(5)也可以使用操作符表示匹配,例如要输出所有辅导员不是Tangwei的学生的信息:
[root@localhost test]# awk '$4 !~/[Tt]angwei/{print $0}' students
2821020225 Liulu Sichuan Lixia 01/23/93 89 76 88 72 325 81
2821020115 Liumin Henan lixia 05/14/94 78 65 59 78 280 70
2721020321 Xuli Jiangsu Luolei 12/25/92 76 81 85 79 321 80
2921020632 Xiayu Shanxi Hetao 03/26/93 78 86 92 78 334 84
2721030227 Wangtao Yunnan Huli 03/21/93 87 76 69 88 320 80
(6)也可以将匹配操作符与正则表达式一起使用。例如输出所有93年以后出生的学生信息:
[root@localhost test]# awk '$5 ~/^.......[3-9]/{print $0}' students
2821020225 Liulu Sichuan Lixia 01/23/93 89 76 88 72 325 81
2821020115 Liumin Henan lixia 05/14/94 78 65 59 78 280 70
2921020632 Xiayu Shanxi Hetao 03/26/93 78 86 92 78 334 84
2921050313 Heli Xizang Tangwei 07/12/94 56 78 80 45 259 65
2721030227 Wangtao Yunnan Huli 03/21/93 87 76 69 88 320 80
(7)使用竖线“|”匹配两边模式之一,例如要输出所有来自Sichuan和Yunnan的学生信息:
[root@localhost test]# awk '$3 ~/(Sichuan|Yunnan)/{print $0}' students
2821020225 Liulu Sichuan Lixia 01/23/93 89 76 88 72 325 81
2721010409 Liwei Sichuan tangwei 11/21/92 98 88 85 85 356 89
2721030227 Wangtao Yunnan Huli 03/21/93 87 76 69 88 320 80
(8)在使用$0标识符输出所有域时,也可以省略后面的输出动作。下面这条命令和上面的命令效果一样:
[root@localhost test]# awk '$3 ~/(Sichuan|Yunnan)/' students
2821020225 Liulu Sichuan Lixia 01/23/93 89 76 88 72 325 81
2721010409 Liwei Sichuan tangwei 11/21/92 98 88 85 85 356 89
2721030227 Wangtao Yunnan Huli 03/21/93 87 76 69 88 320 80
(9)可以使用算术运算符对域进行计算,这在制作一些统计信息时非常有用。例如要计算所有公共课的平均成绩并输出:
[root@localhost test]# awk '{print $1"\t"$2"\t"$6"\t"$7"\t"$8"\t"$6+$7+$8"\t"($6+$7+$8)/3}' students
2821020225 Liulu 89 76 88 253 84.3333
2821020115 Liumin 78 65 59 202 67.3333
2721020321 Xuli 76 81 85 242 80.6667
2921020632 Xiayu 78 86 92 256 85.3333
2721010409 Liwei 98 88 85 271 90.3333
2921050313 Heli 56 78 80 214 71.3333
2721030227 Wangtao 87 76 69 232 77.3333
(10)可以结合使用关系运算符和逻辑运算符,以实现更复杂的运算。例如要输出所有来自Sichuan并且平均分大于80的学生的信息:
[root@localhost test]# awk '($3=="Sichuan" && $11>80){print $0}' students
2821020225 Liulu Sichuan Lixia 01/23/93 89 76 88 72 325 81
2721010409 Liwei Sichuan tangwei 11/21/92 98 88 85 85 356 89
注意:使用awk时,一定要将多个模式和条件放在括号中,将执行编辑的语句放在单引号内,函数和流控制语句放入大括号内,避免产生错误。
三、在awk命令中使用变量
awk中的变量有两种:一种是内置变量,这些变量常用于控制输出和保存“awk”当前工作状态等信息,在引用变量时通常不需要使用美元符号$;另一种是用户自定义变量,这些变量由用户自己定义并使用,通常自定义变量放在BEGIN语句中初始化(赋值)。
使用自定义变量时,如果awk不能确定自定义变量的类型,通常将其默认为字符串进行处理。
- 内置变量:
FILENAME:用于保存输入文件的文件名称。
NF:用于保存当前正在处理的记录的域个数。
NR:用于保存从文本中读取记录的个数。
FNR:用于保存当前读取的记录数。当输入的文件有多个时,读取新文件时,awk会重置这个变量。
OFS:用于设置输出分隔字段的字符,默认为空格。
FS:用于设置字段分隔符。
ORS:用于设置输出记录分隔符,默认为新的一行。
RS:用于设置记录分隔符,默认为新行。
OFMT:数字的输出格式。
ENVIRON:读取环境变量。
(1)有时可能需要对输出的文本重新格式化(设置你想要的输出形式),这时可以使用内置变量OFS和ORS,设置输出文本的域分隔符和记录分隔符。
[root@localhost test]# awk 'BEGIN{FS=":"; OFS="\t"; print "NF\tNR\tusername\tshell"}{print NF,NR,$1,$7}END{print FILENAME}' /etc/passwd
NF NR username shell
7 1 root /bin/bash
7 2 bin /sbin/nologin
7 3 daemon /sbin/nologin
7 4 adm /sbin/nologin
7 5 lp /sbin/nologin
7 6 sync /bin/sync
......
(2)有时会遇到一些使用特殊分隔符的文本。例如下面这个示例中,需要在命令中把FS和RS重新设置成文本中使用的的特殊分隔符(而不是默认的空格和新行)才能顺利读取文本:
[root@localhost test]# cat students3
2821020225#Liulu#0#A#B#0\ 2821020115#Liumi#B#C#0#0\ 2721020321#Xuli#0#D#A#0\ 2921020632#Xiayu#A#C#0#0\ 2721010409#Liwei#B#C#0#D\ 2921050313#Heli#B#0#D#0\ 2721030227#Wangtao#C#0#D#0\
[root@localhost test]# awk 'BEGIN{FS="#"; RS="\\n\n"}{print $1,$2}' students3
2821020225 Liulu
2821020115 Liumi
2721020321 Xuli
2921020632 Xiayu
2721010409 Liwei
2921050313 Heli
2721030227 Wangtao
(3)当输出的字符为一个浮点数时,可能需要指定浮点数的输出格式,此时可以使用内置变量OFMT。例如:
[root@localhost test]# awk 'BEGIN{OFMT="%.2f"; print 19.243564}'
19.24
提示:使用awk命令时,如果没有编辑语句,只有BEGIN语句,可以不必输入文件名直接运行。
(4)有时可能在处理过程中需要引用环境变量,此时可以使用ENVIRON变量。如下是一个使用ENVIRON读取环境变量的示例:
[root@localhost test]# awk 'END{print ENVIRON["LANG"]}' students
en_US.UTF-8
上面这个例子中虽然没有引用文件中的内容,但使用了END表达式,因此必须使用文件参数,否则这条命令将变的不完整。
- 自定义变量
自定义变量通常用于统计并计算某个数字字段的结果,也可用于引用字符串。
(1)下面是一个统计当前文件夹中所有文件占用空间的示例:
[root@localhost test]# ls -l | awk 'BEGIN{A=0}{A=A+$5}END{print "The size of all files of the current directory is:"A}'
The size of all files of the current directory is:13232
在上面这个示例中,首先在BEGIN表达式中定义了一个 变量A并初始化为0.然后与ls命令输出的第5个字段相加,得到所有文件占用的空间,最后在END表达式中将结果输出。
(2)变量也可以在动作语句之后添加,例如:
[root@localhost test]# awk '$2==NAME1{print $0}' NAME1="Liwei" students
2721010409 Liwei Sichuan tangwei 11/21/92 98 88 85 85 356 89
提示:awk中引用变量时,通常不需要使用引用符号。但为了便于理解和阅读,应将变量名称大写并加上引用符号。
四、在awk命令中使用流程控制
在awk命令中,还内置了一些流程控制语句(简称流控制语句)。使用这些流控制语句,可以完成更多复杂的抽取任务。
- awk中的流控制语句语法结构:
awk命令中常见的流控制语句有:if、while、do-while和for语句,这些语句的基本语法结构与c语言中的语句类似。
(1)if语句的基本格式:
if (条件表达式)
{
语句块1
}
else
{
语句块2
}
(2)while语句基本格式:
while (条件表达式)
{
语句块
}
(3)do-while语句基本格式:
do
{
语句块
} while (条件表达式)
(4)for语句基本格式:
for (初始表达式;条件表达式;步长)
{
语句块
}
(5)控制语句:
continue:用于在循环语句中控制循环走向。执行到此语句时,立即从当前执行位置跳转到循环开始处开始下一次循环。此语句用于标识后面的语句已经没有必要执行了,必须立即开始下一次循环执行过程。
break:用于跳出循环。当执行到此语句时,循环立即停止,然后执行循环后面的语句。
next:使awk读取文本的下一行。该语句告诉awk,当前行已经没有任何价值,需要立即读取文本的下一行。
exit:如果这条语句没有出现在END表达式中,则立即跳转去执行END中的语句(即直接跳过文件最后一行,执行END表达式中的语句)。如果出现在END表达式中,则awk命令立即停止执行并退出。
提示:由于循环语句会导致命令变得很长,且影响阅读,因此通常建议将循环语句放入脚本文件中执行。
流控制语句用法示例:
(1)例如要输出文件students中,所有总成绩320以上的学生信息:
[root@localhost test]# awk 'BEGIN{OFS="\t"}{if($10>=320) print $1,$2,$10}' students
2821020225 Liulu 325
2721020321 Xuli 321
2921020632 Xiayu 334
2721010409 Liwei 356
2721030227 Wangtao 320
(2)输出来自Sichuan的学生中,总成绩大于等于320的学生信息:
[root@localhost test]# awk 'BEGIN{OFS="\t"}{if($3=="Sichuan" && $10>=320) print $1,$2,$10}' students
2821020225 Liulu 325
2721010409 Liwei 356
(3)计算ls命令传送过来的信息中,所有普通文件的大小和文件夹的个数:
[root@localhost test]# cat total.awk
BEGIN{
#Initialize A and count.
A=0;
count=0;
}
{
#if the first character of field 1 is '-', then add A and the value of field 5.
if($1 ~/^-/)
A+=$5;
#if the first character of field 1 is 'd', then count plus 1.
if($1 ~/^d/)
count++;
}
END{
print "total:"A;
#count-2:because there are current directory and up level directory.
print count-2," directories.";
}
[root@localhost test]# ls -al /etc | awk -f total.awk
total:1423743
84 directories.
(4)Linux系统管理员经常需要关注网络延时大小,以免对应用服务的正常运作造成影响。最好的解决办法是每隔一段时间,使用ping命令测试与某个服务器的连接,并统计延时。
下面是一个使用ping命令发送4个ICMP数据包测试双向连通性,并用awk从中计算最大延时、最小延时和平均延时的示例:
[root@localhost test]# cat ping.awk
BEGIN{
#使用重新定义变量FS的方法设置域分隔符为冒号、等号及空格
FS="[:= ]";
AVG=0;
MAX=0;
MIN=0;
}
{
for(I=1;I<9;I++)
{
#如果当前为第二条记录,则为变量MAX、MIN、IP_ADDR赋值
if(NR==2)
{
MAX=$11;
MIN=$11;
IP_ADDR=$4;
}
if(NR>1 && NR<6)
{
#计算延时总和
AVG+=$11;
if($11>MAX)
MAX=$11;
if($11<MIN)
MIN=$11;
}
#如果记录数大于6,则跳出循环并执行END中的表达式
if(NR>=6)
exit;
#否则,读取系一条记录,重新开始循环
next;
}
}
END{
AVG=AVG/4;
print "IP address:",IP_ADDR;
print "Avg:",AVG,"ms";
print "Max:",MAX,"ms";
print "Min:",MIN,"ms";
}
为了方便抽取数据,先在BEGIN语句中定义了3中域分隔符:冒号、等号和空格。
由于ping命令返回的第1行是由ping自动生成的简要提示信息,因此awk将忽略第1行。
第2个到第5个记录返回了TTL值和延时等信息,后面则是一些统计信息,因此awk处理过程中只处理第2个到第5个记录。
五、awk命令中的函数
awk命令中的函数分为两种:内置函数和用户自定义函数。内置函数多是一些用来处理字符串和数学运算的函数,用户自定义函数则是由用户自己定义的功能性函数。
- 内置函数:
awk命令中函数的用法与c语言中函数的用法十分相似。常见的内置函数如下:
sub(regexp, replacement [, target]):当模式regexp第1次出现时,用replacement替换。
gsub(regexp, repalcement [, target]):将与模式regexp匹配的内容全部用replacement替换。
index(string, find):返回与find匹配的字符串第1次在string中出现的位置。
length([string]):返回字符串string的长度。
match(string, regexp [, array]):返回与regexp字符串第1次匹配的位置,如果不存在则返回0.
split(string, array [, fieldsep]):将string按照字段分隔符filedsep进行分割,分割后的字段存入数组array,返回字符串数组元素个数。
printf(format, expr1 [, expr2, …]):格式化输出函数。
substr(string, start, [, length]):从string中返回从start开始,长度为length的子字符串。
tolower(string):将string中的大写字符转换成小写。
toupper(string):将string中的小写字符转换成大写。
print:输出函数。
rand():返回一个0到1之间的随机数。
system():将传递来的字符串按shell命令执行。
atan2(x, y):反正切函数。
cos(x):余弦函数。
sin(x):正弦函数。
log(x):x的自然对数值。
exp(x):E的x次幂。
int(x):将x转换为整数类型。
sqrt(x):x的平方根。
用法示例:(示例文件students2.2内容如下)
[root@localhost test]# cat students2.2
2821020225 Liulu 0 A B 0
2821020115 Liumin B C 0 0
2721020321 Xuli 0 0 B D
2921020632 Xiayu 0 0 0 0
2721010409 Liwei C 0 0 0
2921050313 Heli A 0 A B
2721030227 Wangtao B B C 0
(1)使用sub函数可以查找第1次出现的模式并替换。例如用sub函数修改匹配模式的记录:
[root@localhost test]# awk '$1 ~/28210202/{print sub(/Liulu/,"Lilu",$0)"\n"$0}' students2.2
1
2821020225 Lilu 0 A B 0
在上面的命令执行后,在第1行返回了修改的次数(对应sub函数),第2行输出了修改后的记录(对应$0)。
(2)利用函数gsub将所有匹配到的大写字母A替换成数字95,然后返回一共修改的次数:
[root@localhost test]# awk 'BEGIN{N=0}{N+=gsub(/A/,"95",$0); print $0}END{print N}' students2.2
2821020225 Liulu 0 95 B 0
2821020115 Liumin B C 0 0
2721020321 Xuli 0 0 B D
2921020632 Xiayu 0 0 0 0
2721010409 Liwei C 0 0 0
2921050313 Heli 95 0 95 B
2721030227 Wangtao B B C 0
3
由于函数gsub执行一个记录的替换就会返回一次,因此此处用N+=gsub()。否则只会返回最后一条记录替换的次数。
(3)利用index函数可以返回第1次匹配字符串的位置:
[root@localhost test]# awk '{print index($0, "L"); if(NR==2) exit}' students2.2
12
12
(4)利用length函数查看字符串长度:
[root@localhost test]# awk '{print length($0); if(NR==2) exit}' students2.2
25
26
(5)使用match函数查看匹配模式首次出现的位置:
[root@localhost test]# awk 'BEGIN{print match("Hello! Welcome to Beijing!", "B")}'
19
(6)利用split函数将字符串按指定的分隔符拆开,然后放入指定的数组中并返回数组的元素个数:
[root@localhost test]# awk 'BEGIN{print split("FV7H8-42D17-08D0Q-8QAG9-YPUZA",ARR,"-");for(I in ARR)print ARR[I]}'
5
8QAG9
YPUZA
FV7H8
42D17
08D0Q
另一种方式:
[root@localhost test]# awk 'BEGIN{AN=split("FV7H8-42D17-08D0Q-8QAG9-YPUZA",ARR,"-");print AN;for(I=1;I<=AN;I++)print ARR[I]}'
5
FV7H8
42D17
08D0Q
8QAG9
YPUZA
注意:数组下标从1开始。
提示:在awk中使用数组时,可以不用先定义,也不必指出元素的个数。
(7)利用tolower函数将字符串中的大写字符转换成小写:
[root@localhost test]# awk '$1=="2821020225"{print tolower($0)}' students2.2
2821020225 liulu 0 a b 0
(8)利用toupper函数将字符串中的小写字符转换成大写:
[root@localhost test]# awk '$1=="2821020225"{print toupper($0)}' students2.2
2821020225 LIULU 0 A B 0
(9)利用rand函数返回一个0到1之间的随机数:
[root@localhost test]# awk 'BEGIN{print rand()}'
0.237788
(10)sin、cos函数可以计算正弦、余弦:
[root@localhost test]# awk 'BEGIN{PI=3.1415926;print sin(PI/4),cos(PI/4)}'
0.707107 0.707107
(11)使用log函数可以返回自然对数:
[root@localhost test]# awk 'BEGIN{print log(100)}'
4.60517
(12)exp函数可以返回e的x次幂:
[root@localhost test]# awk 'BEGIN{print exp(256)}'
1.51143e+111
(13)sqrt函数可以计算平方根:
[root@localhost test]# awk 'BEGIN{print sqrt(256)}'
16
(14)格式化输出函数printf(与c函数里的printf一样),可以使用修饰符对输出的内容进行格式控制,其基本语法如下:
print format, [argument]…
printf常用的修饰符:
%c:作为一个ASCII字符输出。
%s:作为一个字符串输出。
%o:作为一个八进制数输出。
%x:作为一个十六进制输出。
%d:作为一个整数输出。
%e:作为一个科学型浮点数输出。
%f:作为一个浮点型数字输出。
%g:由awk决定浮点数输出的形式。
除了以上一些修饰符外,awk也允许像c语言那样使用“-”、域宽和“.”进一步细化输出格式。
使用printf函数将数字100分别转换成字符、八进制、十六进制数:
[root@localhost test]# awk 'BEGIN{A=100;printf "%c\n%o\n%x\n",A,A,A}'
d
144
64
(15)使用printf的修饰符可以将生成的数据转换成相应的格式:
[root@localhost test]# awk 'BEGIN{printf "%e\n%f\n%g\n",exp(30),exp(30),exp(30)}'
1.068647e+13
10686474581524.462891
1.06865e+13
(16)有时在计算一个数字时要保留一定的精度,这时可以使用“.”指定精度,例如:
[root@localhost test]# awk 'BEGIN{printf "%4.5f\n",sqrt(47)}'
6.85565
(17)从/etc/passwd中抽取用户名和用户使用的shell,使用%s对齐输出:
[root@localhost test]# awk 'BEGIN{FS=":";printf "%-15s %-15s\n","username","shell";printf "------------------------------\n"}{printf "%-15s %-15s\n",$1,$7}' /etc/passwd
username shell
------------------------------
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
...
(18)、为了使输出的段对齐,这里使用修饰符,例如:
[root@localhost test]# awk '{printf "%-10s\t%-10s\t%d\t%d\t%d\t%d\t%d\n",$1,$2,$6,$7,$8,$6+$7+$8,($6+$7+$8)/3}' students
2821020225 Liulu 89 76 88 253 84
2821020115 Liumin 78 65 59 202 67
2721020321 Xuli 76 81 85 242 80
2921020632 Xiayu 78 86 92 256 85
2721010409 Liwei 98 88 85 271 90
2921050313 Heli 56 78 80 214 71
2721030227 Wangtao 87 76 69 232 77
- 用户自定义函数
在awk命令中,还允许使用用户自定义函数。与C语言中的函数作用范围不同,自定义函数无论出现在命令中的什么位置,作用都是全局的,而函数中定义的变量只作用于函数内部。如果需要从函数中返回一个值,可以使用return语句。
在awk命令中自定义函数的基本格式:
function name([参数列表])
{
语句块;
[return ...];
}
在调用函数时,只需要使用函数名并加上要使用的参数列表即可,无须使用特别的语句。
(1)下面是一个计算文件students中学生公共课总成绩和平均成绩的例子:
[root@localhost test]# cat ex.awk
function Add(A,B,C)
{
return A+B+C;
}
function Avg(A,B,C)
{
return (A+B+C)/3;
}
{
printf "%-10s\t%-10s\t%d\t%d\t%d\t%d\t%d\n",$1,$2,$6,$7,$8,Add($6,$7,$8),Avg($6,$7,$8);
}
END{
printf "%s\n",FILENAME;
}
[root@localhost test]# awk -f ex.awk students
2821020225 Liulu 89 76 88 253 84
2821020115 Liumin 78 65 59 202 67
2721020321 Xuli 76 81 85 242 80
2921020632 Xiayu 78 86 92 256 85
2721010409 Liwei 98 88 85 271 90
2921050313 Heli 56 78 80 214 71
2721030227 Wangtao 87 76 69 232 77
students
(2)对students中的学生成绩进行筛选,只输出学号,学生姓名和课程中的最高成绩并保存为strudents_max.
为实现上述功能,此处可以使用条件三目运算符和数组两种方法。为此因此两个脚本文件stu.awk1和stu.awk2:
[root@localhost test]# cat stu.awk1
function Max(Arr)
{
Ma=Arr[1];
for(I in Arr)
{
if(Ma<Arr[I])
Ma=Arr[I];
}
return Ma;
}
{
for(J=1;J<5;J++)
Ar[J]=$(J+2);
printf "%-10s\t%-10s\t%d\n",$1,$2,Max(Ar);
}
[root@localhost test]# cat stu.awk2
function Max(A,B,C,D)
{
A>B?A:A=B;
C>D?C:C=D;
Ma=(A>C)?A:C;
return Ma;
}
{
printf "%-10s\t%-10s\t%d\n",$1,$2,Max($3,$4,$5,$6);
}
使用awk的选项f调用stu.awk脚本,并将结果显示并保存到students_max中:
[root@localhost test]# awk -f stu.awk1 students | tee students_max
2821020225 Liulu 89
2821020115 Liumin 78
2721020321 Xuli 85
2921020632 Xiayu 92
2721010409 Liwei 98
2921050313 Heli 80
2721030227 Wangtao 88
[root@localhost test]# awk -f stu.awk2 students | tee students_max2
2821020225 Liulu 89
2821020115 Liumin 78
2721020321 Xuli 85
2921020632 Xiayu 92
2721010409 Liwei 98
2921050313 Heli 80
2721030227 Wangtao 88
注意:在C语言中A>B?A:(A=B)可以简写成A>B?:(A=B),但在awk中这是不允许的。
(3)最后,引用一个使用awk监视磁盘并向用户发出警告的例子。在这个例子中,当某一个文件系统空闲不足10%时,通过system函数运行mail命令向管理员发出警告信息。
使用的脚本和执行过程如下:
[root@localhost test]# cat df.awk
function mess(A,B,C)
{
if(B>=0.9)
{
system("date +'%F %r'>/root/df.tmp; df -h>>/root/df.tmp; cat /root/df.tmp | mail -s 'Disk Warning' 1025399680@qq.com,root;rm -rf /root/df.tmp");
printf "Disk Warning!\nFile System:%s\nUsed:%3.0f\nMounted on:",A,B*100,C;
}
}
{
if($1 ~/^\/dev\/sd/)
{
N=$3/$2;
if(N>=0.9)
mess($1,N,$6);
}
}
[root@localhost test]# df | awk -f df.awk
注意:在调用函数system时,一定要将shell命令放入双引号中,shell命令原本应放入双引号中的参数此时应该放入单引号中。
格式化文本数据抽取工具awk的更多相关文章
- 12、Linux基础--挂载磁盘步骤、流处理工具awk(正则 比较 逻辑 算数表达式 流程控制)
笔记 1.晨考 1.用两种方法,实现将文件中的以# 开头的行把# 去掉 sed -r 's/^#//g' /etc/fstab cat /etc/fstab | tr -d '^#' 2.将文件中的H ...
- linux 文本分析工具---awk命令(7/1)
awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大.简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各 ...
- 文本处理工具awk
目录 gawk:模式扫描和处理语言 awk语言 awk工作原理 print awk变量 自定义变量 printf命令 awk操作符 awk的条件判断 awk的循环 awk的数组 awk的函数 awk调 ...
- poi 抽取execl表面数据源代码工具
开发中 ,导入导出execl避免不了数据类型格式的校验,在使用poi要使用抽取表面数据,poi暂时不支持单元格抽取,查询poi源码抽取工具类如下,如使用jxl就不必使用,jxl取出的单元格数据已是抽取 ...
- 文本处理工具-AWK
awk简介 awk功能与sed相似,都是用来进行文本处理的.awk可以自动地搜索输入文件,并把每一个输入行切分成字段.许多工作都是自动完成的,例如读取每个输入行.字段分割. awk工作原理 awk一次 ...
- sqluldr2 oracle直接导出数据为文本的小工具使用
近期客户有需求,导出某些审计数据,供审计人进行核查,只能导出成文本或excel格式的进行查看,这里我们使用sqluldr2工具进行相关数据的导出. oracle导出数据为文本格式比较麻烦,sqluld ...
- Shell 编程 编辑工具 awk
本篇主要写一些shell脚本编辑工具awk的使用. 概述 awk是一个功能强大的编辑工具,逐行读取输入文本,并根据指定的匹配模式进行查找,对符合条件的内容进行格式化输出或者过滤处理. awk倾向于将一 ...
- NLP相关问题中文本数据特征表达初探
1. NLP问题简介 0x1:NLP问题都包括哪些内涵 人们对真实世界的感知被成为感知世界,而人们用语言表达出自己的感知视为文本数据.那么反过来,NLP,或者更精确地表达为文本挖掘,则是从文本数据出发 ...
- 如何使用正则做文本数据的清洗(附免费AI视频福利)
手工打造文本数据清洗工具 作者 白宁超 2019年4月30日09:43:59 前言:数据清理指删除.更正错误.不完整.格式有误或多余的数据.数据清理不仅仅更正错误,同样加强来自各个单独信息系统不同数据 ...
随机推荐
- FastReport.Net使用:[24]其他控件(邮政编码(Zip Code),网格文本(Cellular Text)以及线性刻度尺(Linear Gauge))
邮政编码(Zip Code) Zip Code仅支持数字(0~9) Zip Code支持数据列绑定,表达式,文本等模式 可通过修改SegmentCount属性的值来确定Zip Code的位数. 数字右 ...
- 【BZOJ1098】[POI2007]办公楼biu
题目一开始看以为和强联通分量有关,后来发现是无向边,其实就是求原图的补图的联通块个数和大小.学习了黄学长的代码,利用链表来优化,其实就是枚举每一个人,然后把和他不相连的人都删去放进同一个联通块里,利用 ...
- SPOJ1557 GSS2
不知道第几次回顾了,每次回顾感觉都有新的收获 这题YYZ认为非常的简单,我们一起去%%%她吧(洛谷$id: 54050$) 题面 给出$n$个数,有$q$个询问,求最大子段和,注意相同的数只算一次 做 ...
- 【manacher】模板
考试竟然写错了manacher!太耻辱了!所以赶快又敲了一遍模板!!一定不能错了aaaa #include<iostream> #include<cstdio> #includ ...
- Jeeplus框架中问题解决
1,文件上传后图片显示的问题 (1)问题:在这个组件的文档介绍里面写了这个组件是有预览功能的,但是我没有找到,就自己写了这个小功能. 在框架中下图中,这个文件上传组件只能将文件上传,然后将文件名显示出 ...
- 套题:Codeforces Round #194 (Div. 1) (2/5)
A. Secrets http://www.cnblogs.com/qscqesze/p/4528529.html B. Chips http://www.cnblogs.com/qscqesze/p ...
- 前端UED网站汇总
爱词霸UED团队 MED | 营销展现研究专家 携程UED-携程旅行前端开发团队 支付宝前端开发车间 Taobao UED Team: 淘宝网用户体验团队博客,有关用户体验设计和研究的经验分享. - ...
- layoutit note
Head: JavaScript -> Navbar Menu: JavaScript -> Collapse Compnents -> Panels Compnents -> ...
- CentOS下KVM克隆完成后修改MAC地址/VMware复制虚拟机修改MAC地址
克隆完成之后可能mac地址会有冲突,进入KVM删除/etc/udev/rules.d/70-persistent-net.rules中的eth0的配置,接着把eth1改成eth0,并且修改/etc/s ...
- EntityFramework中几种操作小结
目前项目中使用到的EntityFramework中几种操作小结,先标记下.没有详细介绍,后续有空的话再补充一些并完善一下. 列中加入RowVersion时间戳 public class Product ...