awk简介
awk是一种使用方便且表现力很强的编程语言,它可以应用在多种不同的计算与数据处理任务中。由于awk天生提供对文件中文本分列进行处理,所以如果一个文件中的每行都被特定的分隔符(常见的是空格)隔开,我们可以将这个文件看成是由很多列的文本组成,这样的文件最适合用awk进行处理,其实awk在工作中很多时候被用来处理log文件,进行一些统计工作等。
环境描述
文件file1.txt,存储了个人工作信息,这个文件包含名字,工作时间,每小时工资等信息
Jack 10 12
Alice 8 13
Mary 9 20
Susie 11 10
awk程序的结构
awk都是由一个或者多个模式-动作语句组成的序列:
pattern { action }
pattern { action }
…
1.awk的基本操作是由输入行组成的序列中,陆续的扫描执行每一行,搜索可以被模式匹配的行,每一个输入行轮流被测试一遍,每匹配到一个模式,对应的动作就会执行,然后下一行开始,匹配重新开始,这个过程一直持续到文件被读取完毕为止。通常,模式是可选的,所以动作使用{}括起来,以便区分两者。
2.命令行中的程序被单引号包围时,这个规定可以防止程序中的字符串(例如$)被shell解释,也可以让程序的的长度多于一行。
3.当程序的长度比较短的时候,直接写会比较方便,但是比较长的时候,需要放到文件中,例如文件名为pgfile,这是只要键入
awk –f pgfile
4.模式
模式中可以使用比较符>,>=,==,<,<=,!=,并且还可以使用并且与或者(&&,||)
大于(>=):awk '$2>=10 { print $1 }' file1.txt
大于(>):awk '$2>10 { print $1 }' file1.txt
等于(==):awk '$2==10 { print $1 }' file1.txt
不等于(!=):awk '$2!=10 { print $1 }' file1.txt
小于等于(<=):awk '$2<=10 { print $1 }' file1.txt
小于(<):awk '$2<10 { print $1 }' file1.txt
并且&&:awk '$2=1 && $3<10 { print $1 }' file1.txt (打印的行满足$2>1并且$3<10)
或者||:awk '$2>11 || $3<=10 { print $1 }' file1.txt (打印的行满足:当$2>11或者$3<=10的时候)
特殊说明
1.awk中的$1表示第一个字段,$2表示第二个字段,以此类推,$0表示一整行
2.awk计算当前输入行的字段数量,并记录到一个内置变量NF中,因此程序{print NF}为打印输入行字段数量
3.awk还提供了另外一个变量NR,我们可以使用NR和$0为每一行加上行号{ print $0,NR }
4.单词也可以用于与表达式组合;例如:awk '{ print "Total pay for",$1,"is",$2*$3 }' file1.txt
其它内置函数:
awk定义了很多内置函数,下面我们根据函数类型列出常用的函数,下面的函数只是一部分,完整的函数列表则需要查阅awk的官方文档。
算术:atan2(y,x) 返回 y/x 的反正切。cos(x) 返回 x 的余弦;x 是弧度。sin(x) 返回 x 的正弦;x 是弧度。exp(x) 返回 x 幂函数。log(x) 返回 x 的自然对数。sqrt(x) 返回 x 平方根。int(x) 返回 x 的截断至整数的值。rand() 返回任意数字 n,其中 0 <= n < 1。srand([expr]) 将 rand 函数的种子值设置为 Expr 参数的值,或如果省略 Expr 参数则使用某天的时间。返回先前的种子值。字符串:gsub(reg,str1,str2) 使用str1替换所有str2中符合正则表达式reg的子串sub(reg,str1,str2) 含义与gsub相同,只不过gsub是替换所有匹配,sub只替换第一个匹配index(str,substr) 返回substr在str中第一次出现的索引,注意索引从1开始计算,如果没有则返回0length(str) 返回str字符串的长度,length函数还可以返回数组元素的个数blength(str) 返回字符串的字节数match(str,reg) 与index函数一样,只不过reg使用正则表达式,例如match("hello",/lo/)split(str,array,reg)将str分隔成数组保存到array中,分隔使用正则reg,或者字符串都可以,返回数组长度tolower(str) 转换为小写toupper(str) 转换为大写substr(str,start,length) 截取字符串,从start索引开始的length个字符,如不指定length则截取到末尾,索引从1开始其他:system(command) 执行系统命令,返回退出码mktime( YYYY MM dd HH MM ss[ DST]) 生成时间格式strftime(format,timestamp) 格式化时间输出,将时间戳转换为时间字符串systime() 得到时间戳,返回从1970年1月1日开始到当前时间(不计闰年)的整秒数
printf输出
print用于简单快速输出,如果想要格式化输出,可以使用printf
格式
printf(format,value1,value2…valueN)
format是一个字符串,包含按字面打印的文本,中间散布着格式说明,格式说明符用于说明如何打印。一个格式说明符是一个%,后面跟着几个字符,这些字符控制着一个value的输出格式。格式说明符的数量应该和打印的value一样多
例如:
awk '{ printf("Total pay for s% is %.2f\n",$1,$2*$3) }' file1.txt
但是printf不会自动产生换行符或空格符,用户必须自己创建。
数据验证
awk还是一款数据验证工具
真是的数据总数存在错误的,检查数据是否有合理的值,格式是否正确,这种任务叫做数据验证。
例如:
awk 'NF != 3 { print $0,"Number of fields is not equal to 3" }' file1.txt
BEGIN与END
特殊的模式BEGIN在第一个输入文件的第一行之前被匹配,END在最后一个输入文件的最后一行被处理之后匹配
Linux:/usr/local/sbin # echo -e '11 12 13\n21 22 23' | awk 'BEGIN{ print "col1 col2 col3";print ""}{ print $2,$1,$3 }' col1 col2 col3
12 11 13 22 21 23
此命令将echo输出的内容通过管道传输给awk,然后格式化输出,print ""为打印单独的空行。这里echo命令使用了-e选项的目的就是为了保持字符串中的\n的格式能够生效,否则该换行将被忽略。
Linux:/usr/local/sbin # echo -e "1 2 3" | awk 'BEGIN{ print "begin" }{ print $1 }END{ print "end" }' begin 1 end
awk计算
1.这个程序用于计算工作时间大于等于10h的员工数
Linux:/usr/local/sbin # awk '$2>=10 { cnt=cnt+1 }END{ print cnt,"employees worked more than 10h"}' file1.txt 2 employees worked more than 10h
此处的cnt为自定义的变量,cnt=cnt+1的意思为:每次读取一行,如果满足则依次加1.
2.计算员工的总报酬与平均报酬
Linux:/usr/local/sbin # awk '{ pay=pay+$2*$3 }END{ print "Total pay is ",pay ;print "Average pay is ",pay/NR }' file1.txt Total pay is 514 Average pay is 128.5
当NR值为0的时候,则程序会报错提示除数不为0
3.求出工作时间最长的
Linux:/usr/local/sbin # awk '$2>maxtime {maxtime=$2;maxname=$1}END{ print maxtime,maxname }' file1.txt 11 Susie
变量maxtime保存的是数值,maxname保存的是字符串;
4.拼接字符串
Linux:/usr/local/sbin # awk '{ names=names $1 "@|@"}END{ print names}' file1.txt Jack@|@Alice@|@Mary@|@Susie@|@
此命令是将员工名字以@|@的方式进行拼接,names为初始自定义变量。
流程控制语句
if-else语句
Linux:/usr/local/sbin # echo -e '23 man Jack' | awk '{if($1<18) print "The boy is underage"; else print "The boy has group up"; }' The boy has group up
根据孩子的年龄来判断该孩子是否成年,成年则打印The boy has group up否则打印 The boy is underage(该孩子未成年)
while语句
while含有条件判断和循环体,如果条件为真的话,循环体执行。
Linux:/usr/local/sbin # awk 'BEGIN{ count=1;while(count<=6){ print count;count ++}}' 1 2 3 4 5 6
count初始赋值为1,依次加1,当count超过6,则停止循环;
for语句
Linux:/usr/local/sbin # awk 'BEGIN{ for(i=1;i<=5;i++) print i }' 1 2 3 4 5
常用命令行
awk 'END{print NR}' file1.txt #输入总行数
awk 'NR==2' file1.txt #打印第二行
awk '{print $NF}' file1.txt #打印最后一列
awk 'NF>2' file1.txt #打印字段数多于2个的行
awk '$NF>12' file1.txt #打印字段数多于2个的行
awk '{nf=nf+NF}END{ print nf }' file1.txt #打印输出的字段总个数
awk '/Su/{ line=line+1 }END{ print line}' file1.txt #打印包含字符Su的行的数量
awk '$2>maxnum {maxnum=$2;maxname=$1}END{ print maxnum,maxname}' file1.txt #打印$2最大值
awk 'NR>0' file1.txt #打印至少包含一个字段的行
awk 'length($0)>10' file1.txt #打印长度超过10的行
awk '{ print NF,$0 }' file1.txt #每一行前添加字段数
awk '{ print $3,$2,$1 }' file1.txt #打印每一行的字段,但是顺序相反
awk '{ $1=NR;print }' file1.txt #每一行的第一个字段用行号替换并打印
awk '{ $2="";print }' file1.txt #打印除第二行外的其他行
案例描述
1.现在想求出每个人的当天的报酬信息(并且每个人的工作时间必须大于0)
Linux:/usr/local/sbin # awk '$2>0 {print $1,$2*$3}' file1.txt
Jack 120 Alice 104 Mary 180 Susie 110
该命令行告诉系统运行awk程序,被运行的程序用单引号包围起来,从file1.txt中获取数据,被单引号包围的是一个完整的awk程序,它由一个独立的模式-动作组成。模式$2 > 0扫描每一个输入行,如果该行的第二列大于0,则执行动作{print $1,$2*$3},此时就会为每一行匹配打印第一个字段,以及第二个字段与第三个字段的乘积,如果想知道工作时间为0的是谁,直接使用awk '$2==0 {print $1}' file1.txt即可打印出来。
2.使用printf打印每位员工的名字与报酬
Linux:/usr/local/sbin # awk '{ printf("%-8s $%6.2f\n",$1,$2*$3) }' file1.txt Jack $120.00 Alice $104.00 Mary $180.00 Susie $110.00
第一个格式说明符%-8s,将名字左对齐输出,占用8个字符宽度;第二个格式说明符%6.2f,将报酬以美元并精确小数点2位,占用6个字符长度的方式打印出来。
3.打印出报酬,并升序排序
Linux:/usr/local/sbin # awk '{ printf("%6.2f %s\n",$2*$3,$0)}' file1.txt | sort -n 104.00 Alice 8 13 110.00 Susie 11 10 120.00 Jack 10 12 180.00 Mary 9 20
awk的输出通过管道传输给sort,排序后输出,-n是升序排序,如果需要降序排序,使用-r即可。
4.打印文件的最后一行
Linux:/usr/local/sbin # awk '{ last=$0 }END{ print last }' file1.txt Susie 11 10