shell中文本处理三剑客---之---awk命令详解

1.awk介绍

awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”

Linux awk命令详解除了使用 sed 命令,Linux 系统中还有一个功能更加强大的文本数据处理工具,就是 awk。它诞生于 20 世纪 70 年代末期,这也许是它影响了众多 Linux 用户的原因之一
"
和 sed 命令类似,awk 命令也是逐行扫描文件(从第 1 行到最后一行),寻找含有目标文本的行,如果匹配成功,则会在该行上执行用户想要的操作;反之,则不对行做任何处理

它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。

awk 是一种很棒的语言,它适合文本处理和报表生成,其语法较为常见,借鉴了某些语言的一些精华,如 C 语言等。在 linux 系统日常处理工作中,发挥很重要的作用,掌握了 awk将会使你的工作变的高大上。

awk程序由一个主输入循环(Main input loop)维持,主输入循环反复执行,直到条件被触发,主输入循环无须由程序员去写,awk已经搭好主输入循环的框架
awk 命令的基本格式为:

[root@localhost ~]# awk [选项] '脚本命令' 文件名

awk 的强大之处在于脚本命令,它由 2 部分组成,分别为匹配规则和执行命令,如下所示

‘匹配规则{执行命令}’

这里的匹配规则,和 sed 命令中的 address 部分作用相同,用来指定脚本命令可以作用到文本内容中的具体行,可以使用字符串(比如 /demo/,表示查看含有 demo 字符串的行)或者正则表达式指定。另外需要注意的是,整个脚本命令是用单引号(’’)括起,而其中的执行命令部分需要用大括号({})括起来。

在 awk 程序执行时,如果没有指定执行命令,则默认会把匹配的行输出;如果不指定匹配规则,则默认匹配文本中所有的行

#awk已经搭好主输入循环框架,程序员写的代码被嵌入到主输入循环框架中执行 主输入循环自动依次读取输入文件行,以供处理,而处理文件行的动作是由程序员添加的

#awk认为输入文件是结构化的,awk将每个输入文件行定义为记录,行中的每个字符串定义为域,域之间用空格,Tab键或其他符号进行分隔,分隔域的符号就叫做分隔符

#F选项是改变分隔符 -f选项则表示调用awk脚本
#awk环境变量FS 可通过在BEGIN字段中设置FS的值来改变分隔符

#BEGIN语句将FS赋值为逗号,表示以逗号分隔符来处理文件 print语句设置需要打印的域号

[root@server1 ~]# awk 'BEGIN {FS=","} {print $0}' stufile 
Tom,18,029-11110239
Tom,18,029-11110239
Tom,18,029-11110239
Tom,18,029-11110239
Tom,18,029-11110239
Tom,18,029-11110239
Tom,18,029-11110239
[root@server1 ~]# awk 'BEGIN {FS=","} {print $1,$3}' stufile 
Tom 029-11110239
Tom 029-11110239
Tom 029-11110239
Tom 029-11110239
Tom 029-11110239
Tom 029-11110239
Tom 029-11110239
$n:当前记录的第n个域 域间由FS分隔
$0:记录所有的域
FS:字段分隔符,默认是空格键
NF:当前记录中的域数量
NR:当前记录数(被awk处理过的行数)

2.awk模式匹配

任何awk语句都由模式(pattern)和动作(action)组成
模式是由一组用于测试输入行是否需要执行动作的规则
动作是包含语句,函数和表达式的执行过程
简言之,模式决定动作何时触发和触发事件
动作执行对输入行的处理
awk的第一种调用方式
单引号中间是awk命令,该awk命令由两部分组成
‘/’符号分隔,^$部分是模式,花括号部分是动作,该awk命令表示一旦读入的输入文件行是空行,就打印“This is a blank line”

^$是正则表达式,表示空白行
print表示该动作是打印操作,input是输入文件名称
[root@server1 ~]# touch input
[root@server1 ~]# awk '/^$/{print "This is a blank line."}' input
[root@server1 ~]# vim input 
[root@server1 ~]# awk '/^$/{print "This is a blank line."}' input
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
[root@server1 ~]# cat input 
 
[root@server1 ~]# 

awk的第二种调用方式
使用awk -f 调用scr.awk文件

[root@server1 ~]# vim scr.awk
[root@server1 ~]# awk -f scr.awk input 
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
[root@server1 ~]# cat scr.awk 
/^$/{print "This is a blank line."}

直接使用awk脚本文件的方法来调用

 [root@server1 ~]# vim scr.awk 
[root@server1 ~]# chmod o+x scr.awk 
[root@server1 ~]# ./scr.awk input 
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
[root@server1 ~]# cat scr.awk 
#!/bin/awk -f
/^$/{print "This is a blank line."}

3.记录和域

awk认为输入文件是结构化的,awk将每个输入文件行定义为记录,行中的每个
字符串定义为域,域之间用空格,Tab键或其他符号进行分隔,分隔域的符号就叫做分隔符
awk定义域操作符$来指定执行动作的域,域操作符$后面跟数字或变量来标识域的位置,每条记录的域从1开始编号,如$1表示第一个域 $0表示所有域

打印指定的域

[root@server1 ~]# awk '{print $2,$1,$4,$3}' stucoed 
Li Tom xian 028-12345678
Li Tom xian 028-12345678
Li Tom xian 028-12345678
Li Tom xian 028-12345678
Li Tom xian 028-12345678
Li Tom xian 028-12345678
Li Tom xian 028-12345678
Li Tom xian 028-12345678
[root@server1 ~]# cat stucoed 
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian

打印全部的域

 [root@server1 ~]# awk '{print $0}' stucoed Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian

域操作符$后面还可以跟变量,或者变量运算表达式

[root@server1 ~]# awk 'BEGIN {one=1;two=2} {print $(one+two)}' stucoed 
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678

awk默认的设置是空格键,Tab键被看作连续的空格键来处理

[root@server1 ~]# cat stucoed 
Tom Li         028-12345678  xian
Tom Li         028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
Tom Li 028-12345678  xian
[root@server1 ~]# awk 'BEGIN {one=1;two=2} {print $(one+two)}' stucoed 
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678

可以使用-F来改变分隔符
注意看下面的例子 电话和xian之间用空格分开的化被看作是一个域

[root@server1 ~]# awk -F "\t" '{print $3}' stucoed 
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
028-12345678
[root@server1 ~]# cat stucoed
Tom Li        028-12345678    xian
Tom Li        028-12345678    xian
Tom Li        028-12345678    xian
Tom Li         028-12345678    xian
Tom Li         028-12345678    xian
Tom Li        028-12345678    xian
Tom Li        028-12345678    xian
Tom Li        028-12345678    xian
[root@server1 ~]# vim stucoed[root@server1 ~]# cat stucoedTom Li        028-12345678 xian
Tom Li        028-12345678 xian
Tom Li        028-12345678 xian
Tom Li         028-12345678 xian
Tom Li         028-12345678 xian
Tom Li        028-12345678 xian
Tom Li        028-12345678 xian
Tom Li        028-12345678 xian
[root@server1 ~]# awk -F "\t" '{print $3}' stucoed 
028-12345678 xian
028-12345678 xian
028-12345678 xian
028-12345678 xian
028-12345678 xian
028-12345678 xian
028-12345678 xian
028-12345678 xian

尽管-F选项可以改变分隔符 但是 awk还提供了另一种更方便,实用的方法来改变分隔符,这就是使用awk环境变量FS
我们可以通过在BEGIN字段中设置FS的值来改变分隔符

[root@server1 ~]# awk 'BEGIN {FS=","} {print  $0}' stucoed 
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian
Tom,Li,028-12345678 xian

打印第1域和第3域

[root@server1 ~]# awk 'BEGIN {FS=","} {print  $1,$3}' stucoed 
Tom 028-12345678 xian
Tom 028-12345678 xian
Tom 028-12345678 xian
Tom 028-12345678 xian
Tom 028-12345678 xian
Tom 028-12345678 xian
Tom 028-12345678 xian
Tom 028-12345678 xian

对于不同的输入文件 需要根据实际情况设置相应的分隔符 如linux的/etc/passwd文件是以冒号为分隔符的

4.关系和布尔运算符

awk定义了一组关系运算符用于awk模式匹配

#使用匹配正则表达式符号
第一个域匹配root

[root@server1 ~]# awk 'BEGIN {FS=":"} $1~/root/' /tmp/passwd 
root:x:0:0:root:/root:/bin/bash

全部域匹配root

[root@server1 ~]# awk 'BEGIN {FS=":"} $0~/root/' /tmp/passwd 
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

全部域不匹配nologin

[root@server1 ~]# awk 'BEGIN {FS=":"} $0!~/nologin/' /tmp/passwd 
root:x:0:0:root:/root:/bin/bash
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
dd:x:1000:1000::/home/dd:/bin/bash

awk在进行模式匹配时,常常使用到条件语句
awk条件语句与C语言类似

注意:if后面的条件需要用圆括号括起来

[root@server1 ~]# awk 'BEGIN {FS=":"} {if ($3<$4) print $0}' /tmp/passwd 
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

多条件精确匹配
在文件中查找满足$3==1或者$4==10的记录
利用==符号进行的匹配可称为精确匹配

[root@server1 ~]# awk 'BEGIN {FS=":"} {if ($3==1||$4==10) print $0}' /tmp/passwd 
bin:x:1:1:bin:/bin:/sbin/nologin

多条件模糊匹配
==改为~符号 表示模糊匹配 所查询内容包含1或者10这个字符就可以

[root@server1 ~]# awk 'BEGIN {FS=":"} {if ($3~1||$4~10) print $0}' /tmp/passwd 
bin:x:1:1:bin:/bin:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
dd:x:1000:1000::/home/dd:/bin/bash

5.表达式

与其他编程语言一样,awk表达式用于存储,操作和获取数据
一个awk表达式可由数值,字符常量,变量,操作符 函数和正则表达式自由组合而成
变量是一个值的标识符,定义awk变量非常方便,只需定义一个变量名并将值赋给它即可
变量名只能包含字母,数字和下划线 而且不能以数字开头
定义awk变量无须声明变量类型,每个变量有两种类型的值:字符串值和数值
awk根据表达式上下文来确定使用哪个值
变量的默认数值为0,默认字符串值为空

统计input文件中的空白行

[root@server1 ~]# awk '/^$/{print x+=1}' input 
1
2
3
4
5
6
7
8
9

++x /x++ 和–x/x-- 的区别

x++:即返回x值后,x变量增加1
++x:即x变量增加1,再返回x值
[root@server1 ~]# awk '/^$/{print x++}' input 
0
1
2
3
4
5
6
7
8
[root@server1 ~]# awk '/^$/{print ++x}' input 
1
2
3
4
5
6
7
8
9

计算平均值的例子

[root@server1 ~]# vim scr2.awk 
[root@server1 ~]# cat stucoed 
Tom,Li,028-1234567 85,92,97,88
Tom,Li,028-1234567 85,92,97,88
Tom,Li,028-1234567 85,92,97,88
Tom,Li,028-1234567 85,92,97,88
Tom,Li,028-1234567 85,92,97,88
Tom,Li,028-1234567 85,92,97,88
Tom,Li,028-1234567 85,92,97,88
Tom,Li,028-1234567 85,92,97,88
Tom,Li,028-1234567 85,92,97,88
Tom,Li,028-1234567 85,92,97,88
Tom,Li,028-1234567 85,92,97,88
Tom,Li,028-1234567 85,92,97,88
Tom,Li,028-1234567 85,92,97,88
Tom,Li,028-1234567 85,92,97,88
[root@server1 ~]# vim scr2.awk 
[root@server1 ~]# vim stucoed 
[root@server1 ~]# vim scr2.awk 
[root@server1 ~]# chmod +x scr2.awk  #给脚本执行权限
[root@server1 ~]# ./scr2.awk stucoed  #输出学生姓名和平均成绩
Tom 72.4
Tom 72.4
Tom 72.4
Tom 72.4
Tom 72.4
Tom 72.4
Tom 72.4
Tom 72.4
Tom 72.4
Tom 72.4
Tom 72.4
Tom 72.4
Tom 72.4
[root@server1 ~]# cat scr2.awk 
#!/bin/awk -f
BEGIN {FS=","}
{total=$4+$5+$6+$7+$8
avg=total/5
print $1,avg}

6.系统变量

awk定义了很多内建变量用于设置环境信息,我们称它为系统变量
这些系统变量可分为:第一种用于改变awk的默认值,如域分隔符
第二种用于定义系统值,在处理文本时可以读取这些系统值 如记录中的域数量,当前记录数,当前文件名等

NF:为记录的域数量
NR:显示当前的记录数,该值根据读取输入文件的进度而变化 读取第一条记录时,NR=1 读取到文件末尾时,NR为该文件所包含的记录数
$0: 标示打印记录的所有域
FILENAME:保存了当前的输入文件名

[root@server1 ~]# awk 'BEGIN {FS=","} {print NF,NR,$0} END {print FILENAME}' stucoed 
7 1 Tom,Li,028-1234567,85,92,97,88
7 2 Tom,Li,028-1234567,85,92,97,88
7 3 Tom,Li,028-1234567,85,92,97,88
7 4 Tom,Li,028-1234567,85,92,97,88
7 5 Tom,Li,028-1234567,85,92,97,88
7 6 Tom,Li,028-1234567,85,92,97,88
7 7 Tom,Li,028-1234567,85,92,97,88
7 8 Tom,Li,028-1234567,85,92,97,88
7 9 Tom,Li,028-1234567,85,92,97,88
7 10 Tom,Li,028-1234567,85,92,97,88
7 11 Tom,Li,028-1234567,85,92,97,88
7 12 Tom,Li,028-1234567,85,92,97,88
7 13 Tom,Li,028-1234567,85,92,97,88
7 14 Tom,Li,028-1234567,85,92,97,88
stucoed

7.格式化输出

前面的例子只涉及awk如何输入文件进行处理,对于输出的格式并未规定
而awk的一大主要功能是产生报表,报表就是要求按照于定的格式输出,awk借鉴
c语言的语法,定义了printf输出语句,它可以规定输出的格式

从域号获取值 $2号域与%s对应 为字符串
$8号域与%d对应 为整数值
两个域之间用Tab键隔开(\t:Tab键)
每输出两个域换行(\n表示换行)

[root@server1 ~]# awk 'BEGIN {FS=","} {printf("%s\t%d\n",$2,$8)}' stucoed 
Li    0
Li    0
Li    0
Li    0
Li    0
Li    0
Li    0
Li    0
Li    0
Li    0
Li    0
Li    0
Li    0
Li    0

ASCII字符的转换

[root@server1 ~]# awk 'BEGIN {printf("%c\n",65)}' 
A

浮数的转换

[root@server1 ~]# awk 'BEGIN {printf("%f\n",2010)}' 
2010.000000

printf修饰符的例子
以字符串格式输出文件的第1和第3号域,并对第1个%s进行了修饰
-15表示该字符串长度控制为15位,并且左对齐,若字符串不足15位 则用空格补齐

[root@server1 ~]# awk 'BEGIN {FS=","} {printf("%-15s\t%s\n",$1,$3)}' stucoed 
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
[root@server1 ~]# awk 'BEGIN {FS=","} {printf("%-15s\t%s\n",$1,$3)}' stucoed 
Tommmm             028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567

如果我们要在输出的域名上加解释语言 可以在BEGIN字段中添加相应的输出注释

[root@server1 ~]# awk 'BEGIN {FS=",";print "Name\t\tPhonenumber"} {printf("%-15s\t%s\n",$1,$3)}' stucoed 
Name        Phonenumber
Tommmm             028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567
Tom                028-1234567

8.内置字符串函数

awk提供了强大的内置字符串函数 用于实现文本的字符串替换 查找以及分割等功能

8.1
gsub函数执行字符串替换功能,它将第一个字符串替换为第二个字符串,gsub函数有两种
形式,第一种形式作用于全部域 即$0
第二种形式作用于域t
OFS:输出域分隔符 默认是空格
替换第一域上的root字符串

[root@server1 ~]# awk 'BEGIN {FS=":";OFS=":"} gsub(/root/,"dd", $1) {print $0}' /tmp/passwd 
dd:x:0:0:root:/root:/bin/bash

替换全部域上的root字符串

[root@server1 ~]# awk 'BEGIN {FS=":";OFS=":"} gsub(/root/,"dd") {print $0}' /tmp/passwd 
dd:x:0:0:dd:/dd:/bin/bash
operator:x:11:0:operator:/dd:/sbin/nologin

8.2 index和length函数的用法
index返回第二个字符串在第一个字符串出现的首位置

[root@server1 ~]# awk 'BEGIN {print index("gridsphere","ph")}'
6

length返回字符串的长度

[root@server1 ~]# awk 'BEGIN {print length("gridsphere")}'
10

8.3
match(s,t)测试s是否包含t的字符串
t可以是一个正则表达式 若匹配成功 返回匹配t的首位置
若匹配不成功 则返回0
在gridsphere字符串中匹配D/d
awk默认状态是区分大小写的

[root@server1 ~]# awk 'BEGIN {print match("gridsphere",/D/)}'
0 #匹配不成功返回0
[root@server1 ~]# awk 'BEGIN {print match("gridsphere",/d/)}'
4

先定义str变量,赋值,然后用sub函数讲pro改成大写的PRO str有两个pro
结果显示只有一个pro改成了大写的PRO

[root@server1 ~]# awk 'BEGIN {str="multiprocessor programming";sub(/pro/,"PRO",str);printf("%s\t",str)}'
multiPROcessor programming    
[root@server1 ~]# 
发布了94 篇原创文章 · 获赞 1 · 访问量 1768

猜你喜欢

转载自blog.csdn.net/qq_36417677/article/details/104417763