linux三剑客之awk命令

awk与sed相似,都是用作文本处理,awk的基本功能也是搜索文件中包含某些模式的行,当某行匹配一个模式时,awk就在那一行上执行指定的操作。

awk语法由一个模式(pattern)后面跟一个动作(action)组成,其中动作被包括在花括号内,用来与模式分离,最后加上输入的文件名

awk模式匹配经常用到正则表达式,支持所有的正则表达式元字符以及”?”和”+” 两个扩展元字符,grep和sed则不支持”?” 和”+” 扩展元字符。

例如:匹配a.txt文件中的空白行后打印hello world,执行命令如下:

上述命令中为awk的第一种调用方式,单引号中间的是awk命令,由两部分组成,通过符号/分割,其中^$表示模式,是正则表达式,花括号中的是动作,print表示打印操作

第二种调用方式是通过脚本的形式,脚本名称test.awk ,脚本内容如下:

上图种直接将模式以及动作写入到脚本文件中,然后通过awk -f 进行调用即可

还可以通过类似shell脚本的写法,以sha-bang符号开头,后面接awk解释器位置,解释器可通过which awk查看,如图:

给脚本赋予执行权限chmod +x test.awk , 然后执行脚本,如图:

awk记录和域

awk对于每个输入文件行定义为记录,对于文件行中的每个字符串定义为域,域之间通过空格、tab键或其他符号进行分割

上图中第一个域为hello、第二个域为world、第三个域为ni,以此类推,通过$符号来指定执行动作的域,如$1表示第一个域、$2表示第二个域,以此类推,$0表示打印所有域

1.例如:通过awk命令,打印域值,按照6、5、4、3、2、1的顺序,如图:

上述命令中print后面打印多个值的时候,每个值中间用逗号分隔即可

2. 如果打印所有域,可以使用$0,如图:

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

上述命令中先用BEGIN定义变量,print后面跟变量表达式,因此实际awk打印的是第3个域值

注意:BEGIN语句在遍历输入文本之前执行的,因此先定义变量,后执行表达式

4. 上图中的d.txt文件中的域默认都是空格进行分割,现在将内容改为如下:

从图中可以看到,hello与world中间通过冒号(:)进行分割,现在我们通过-F选项指定分隔符来打印域值,如图:

上图中通过-F指定冒号(:)为分隔符,打印第一个域和第二个域的值,可以看到hello变为了第一个域,其余几个变成了第二个域

5. 除了-F选项,还可以使用awk中的环境变量FS来改变分隔符,通过在BEGIN中设置FS的值来定义分隔符,如图:

上图中在BEGIN中指定FS为冒号(:)作为分隔符,并且打印第2个域,注意外层单引号内层为双引号,因此FS位置为双引号,否则报错

关系和布尔运算符

awk定义了一组关系运算符用于模式匹配,运算符及其含义如下

运算符含义
<小于
>大于
<=小于等于
>=大于等于
==等于
!=不等于
~匹配正则表达式
!~不匹配正则表达式

本例子中以/etc/passwd文件作为操作对象进行演示

1.打印/etc/passwd中第一个域匹配root的记录,执行命令如下:

上图中$1表示第一个域的值,后面表示匹配root记录

2. 打印全部域匹配root的记录,执行命令如下:

上图中$0表示全部域中匹配root的记录

3. 打印所有域中不匹配nologin关键字的记录,执行命令如下:

4. 打印第七个域匹配bash的记录行的第一个域,执行命令如下:

注意:在使用这些运算符的时候,左侧指定一个变量,右侧指定正则表达式或者字符串

5. awk在进行模式匹配的时候,经常使用条件语句,有if、if/else、if/else else三种。

例如:匹配第三个域的值小于第四个域的值的所有记录,执行命令如下:

awk布尔运算符及其含义

运算符含义
&&逻辑与
||逻辑或
逻辑非

6. 打印匹配第三个域或者第四个域等于1的所有域,执行命令如下

7. 打印第三个域不等于8的所有域,执行命令如下:

8.打印第三个域不等于8并且第四个域不等于99的所有行,如图:

awk表达式

awk表达式用于存储、操作、和获取数据,表达式可由数值、变量、操作符、字符常量、函数、以及正则表达式自由组成。

变量是一个值的标识符,定义变量awk变量很方便,只需要定义一个变量名并将值赋值给它即可,变量名只能包含字母、数字、下划线,但是不能以数字开头,同时变量名是区分大小写的,定义awk变量无需声明变量类型,每个变量有两种类型的值:字符串和数值,变量的默认数值为0,默认字符串为空

awk算术表达式及其含义

运算符含义
+
*
/
%
^或**乘方
++x在返回x值之前,x变量加1
x++在返回x值之后,x变量加1

1.统计文件a.txt中空白行,执行命令如下:

上述命令表示当匹配到空行的时候,执行表达式x+=1,也就是x=x+1,并打印返回值,图中表示文件有三个空白行(默认x的值从0开始)

2.通过x++或者++x命令来统计文件中的空白行,执行命令如下:

上述命令中x++表示返回x的值后,x变量加1,x的初始默认值为0

上述命令中++x表示x变量先加1后,再返回x的值,因此打印出来的值都会加1

通过脚本计算平均值:现有一个文件aa.txt,内容如下:

脚本名称为avg.awk,脚本内容如下:

从脚本内容中可以看出,通过逗号作为分隔符,首先定义变量total,计算所有域的和,再次定义变量avg,取total数的平均值,最后通过print进行打印。

系统变量:

awk系统变量可以分为两种,第一种用于改变awk的默认值,如域分割符,第二种用于定义系统值,在处理文本的时候可以读取这些系统值,如域数量、当前记录数、当前文件名等,awk动态改变第二种系统变量的值

awk环境变量及其含义

变量名含义
$n当前记录的第n个域,域用FS分割
$0记录的所有域
ARGC命令行参数的数量
ARGIND命令行中当前文件位置(以0开始标号)
ARGV命令行参数的数组
CONVFMT数字转换格式
ENVIRON环境变量关联数组
ERRNO最后一个系统错误的描述
FIELDWIDTHS字段宽度列表,以空格键分割
FILENAME当前文件名
FNR浏览文件的记录数
FS字段分隔符,默认是空格
IGNORECASE布尔变量,如果为真,忽略大小写匹配
NF当前记录的域数量,当前行字段数
NR当前记录数,实际执行时对应于当前行号
OFMT数字的输出格式
OFS输出域分隔符,默认空格
ORS输入记录分隔符,默认换行符
RS记录分隔符,默认空格键
RLENGTH由match函数所匹配的字符串长度
RSTART由match函数所匹配的字符串第一个位置
SUBSEP数组下标分隔符,默认是\034

例如:文件a.txt的内容如下:

1、打印当前记录有多少行,也就是文件的行数,如图:

2、打印每个记录行中的域的数量,默认是以空格作为分隔符,如图:

a.txt中第一行有三个域,第二行有2个域,第三行有一个域

3、手动设置为逗号为分隔符,然后打印域的个数,如图:

4、打印最后一个域,如下:

注:使用$NF即可打印最后一个域

5、去掉最后一个域和冒号,如下:

echo "field1:field2:field3:field4" | awk -F':' '{for(i=1;i<NF;i++){printf "%s",$i;if(i< NF-1) printf ":"};print ""}'

格式化输出:

awk一大主要功能是产生报表,awk定义了printf输出语句,此语句可以规定输出的格式,基本语法如下:printf (格式控制符,参数),格式控制符以%符号开始

awk格式控制符分为修饰符以及格式符两种

printf修饰符及其含义

修饰符含义
左对齐
width域的步长
.prec小数点右边的位数

printf格式符及其含义

格式符含义
%cASCII字符
%d整型数
%e浮点数,科学计数法
%f浮点数
%o八进制数
%s字符串
%x十六进制数

例子:现有一文件bb.txt,文件内容如下:

执行命令,打印输出记录行中的第一个域(字符串)以及第三个域(整数),如图:

上图中以逗号为分隔符,%s表示打印字符串,与$1对应,%d表示打印整数,与$4对应,\t表示打印完第一个字符串之后加一个tab键,\n表示打印完第一个记录行数据后,进行换行。注意使用的是printf命令,同时格式控制符通过双引号引起来

执行命令对第一个字符串进行修饰,将字符串长度控制在10位,不足用0补齐,如图:

内置字符串函数

awk提供了强大的内置字符串函数,用于文本中字符串的替换、查找以及分隔功能

awk字符串函数及其含义

函数名含义
gsub(r,s)在输入文件中用s替换r
gsub(r,s,t) 在t中用s替换r
index(s,t)返回s中字符串第一个t的位置
length(s)返回s的长度
match(s,t)测试s是否包含匹配t的字符串
split(r,s,t)在t上将r分成序列s
sub(r,s,t)将t中第一次出现的r替换成s
substr(r,s)返回字符串r中从s开始的后缀部分
substr(r,s,t) 返回字符串r中从s开始长度为t的后缀部分

将/etc/passwd文件中第一个域中的root字符串替换成test,执行命令如图:

上图中OFS表示打印域分隔符,注意FS与OFS之间用分号间隔,gsub函数表示匹配root字符串替换成test,$1表示第一个域,$0表示打印所有匹配的域

如果要替换全部的root字符串为test,只需要将$1去掉即可,如图:

index()函数表示返回第二个字符串在第一个字符串出现的位置,注意要使用BEGIN命令,如图:

length()函数用于计算字符串的长度,如图:

标签