【Linux】awk详细介绍

时间:2021-09-22 14:59:52

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开始计算,如果没有则返回0
length(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