文本处理之awk命令高级用法

文本处理之awk命令高级用法

1. 基本用法

例如,有一个文件的内容是这样的。

[root@kiwi222 ~]# cat 111 
this is hello world

现在将里面的hello world提取出来

[root@kiwi222 ~]# awk '{ print "hello world"}' 111 
hello world
[root@kiwi222 ~]# awk '{ print "hello,world"}' 111 
hello,world
[root@kiwi222 ~]# 

[root@kiwi222 ~]# awk '{ print }' 111 
this is hello world
[root@kiwi222 ~]# 

## print是将提取的给打印出来,print语句没有参数,只简单的输出每个输入行

从上面的例子可以看出awk是输入驱动的。也就是说,除非有可以在其上操作的输入行,否则将什么也不能做。

当调用 awk 程序时,它将读入所提供的脚本并检查其中的指令的语法。然后 awk 将对每个输入行执行脚本中的指令。因此,如果没有来自文件中的输入行,以上的 print 语句将不做任何事情。

2. awk程序设计模型

awk 程序是由所谓的主输入 (main input) 循环组成的。一个循环是一个例程,它将一直重复执行知道有一些存在的条件终止它。

其程序设计遵循以下模型:

  1. 读取输入:AWK程序从输入文件或标准输入读取文本行,逐行处理。
  2. 分割字段:默认情况下,AWK将输入行根据空格(或其他指定的字段分隔符)分割为多个字段。
  3. 匹配模式:使用模式(可以是正则表达式)来匹配符合特定条件的行。可以在模式操作之前或之后执行相关的操作。
  4. 执行操作:针对匹配的行,AWK执行指定的操作。这些操作可以是内置的,也可以是自定义的函数。
  5. 处理字段:在操作过程中,可以直接访问并处理单个字段,例如打印、计算、赋值等操作。
  6. 控制流程:AWK程序支持条件语句(如if-else、while等)和循环语句,以根据需要控制程序的流程。
  7. 输出结果:根据需要,AWK可以通过print语句或其他方式将结果输出到屏幕上或写入输出文件。
  8. 重复步骤:AWK程序会重复执行以上步骤,直到处理完所有的输入行。

awk 允许你编写两个特殊的例程,他们在任何输入被读取前和所有输入都被读取后执行。他们是与 BEGIN 和 END 规则相关的过程。换句话说,在主输入循环执行前和主输入循环钟之后你可以做一些处理。BEGIN和 END 过程是可选的。

也就是说

可以把awk脚本看做由3个主要部分组成:

  1. 处理输入前将要做的处理

  2. 处理输入过程中将做的处理

  3. 处理输入完成后做的处理

例如:

[root@kiwi222 ~]# awk 'BEGIN{print "hello world"}'
hello world

上面这个例子中可以把它看成三个部分

BEGIN为第一个部分,主体为第二个部分,END为第三个部分

  1. 如果我们写到BEGIN里面,它就不需要任何处理的对象。

  2. 如果我们写到主体里面去,它就必须要有操作对象。

  3. END就是等我们主体处理完之后做一个总结

但用的时候可只要BEGIN或者只出现主体,不能只写END。

写法如:
awk 'BEGIN{}{主体}END{}'
3. 模式匹配

当 awk 读入一行是时,它试图匹配脚本中的每个模式匹配规则。只有与一个特定的模式相匹配的输入行才能成为操作对象。如果没有指定操作,于模式相匹配的输入行将被打印出来 (执行打印语句是一个默认操作)

例如:

[root@kiwi222 ~]# cat kiwi222 
kiwi123456

abc123

kiwi111:kiwi222

111:222
[root@kiwi222 ~]# 

现将上面的文件指定操作

[root@kiwi222 ~]# awk '/^$/{ print "Null line"}' kiwi222 
Null line
Null line
Null line

## 这里的意思就是如果文件kiwi222有空行的话就打印Null line这句话
/[0-9+]/ { print "This is number"}
## 匹配如果是数字则打印This is number这句话
4.记录和字段

在awk中,假设它的输入是有结构的,而不只是一串无规则的字符。

那么它会有字段以及记录。记录(record)是指输入数据中的一行文本,而字段(field)则是该行文本中由分隔符(默认是空格)分隔的部分(用来分隔字段的字符被称为分隔符)。

连续的两个或多个空格和/或制表符被作为一个分隔符。

4.1 字段和引用的分离

awk可以使用 $ 符号访问和处理这些字段。

例如,**$**1表示第一个字段,$2表示第二个字段,依此类推。

“$0“则表示整个输入记录

例如:

[root@kiwi222 ~]# cat kiwi333 
kiwi111 123456 //abc
[root@kiwi222 ~]# awk '{ print $2, $3, $1 }' kiwi333 
123456 //abc kiwi111
[root@kiwi222 ~]# 
[root@kiwi222 ~]# awk '{ print $0 }' kiwi333 
kiwi111 123456 //abc
[root@kiwi222 ~]# 

## 这里的值中输出的逗号默认为空格

还可以用任何计算值为整数的表达式来表示一个字段,而不只是用数字和变量。

[root@kiwi222 ~]# echo a b c d | awk 'BEGIN { one = 1; two = 2 } { print $(one + two) }'
c
[root@kiwi222 ~]# 

[root@kiwi222 ~]# awk 'BEGIN{print 10/4}'
2.5
[root@kiwi222 ~]# 

还可以使用-F选项改变字段的分隔符

## 在它们中间加上:
[root@kiwi222 ~]# cat kiwi333 
kiwi111 123456 //abc
kiwi111:123456://abc
[root@kiwi222 ~]# 
[root@kiwi222 ~]# awk -F ':' '{ print $1 }' kiwi333 
kiwi111 123456 //abc
kiwi111
[root@kiwi222 ~]# awk -F ':' '{ print $2 }' kiwi333 

123456
4.2 字段的划分FS

在awk中,可以使用3个完全不同的方法使awk分隔字段。

  1. 第一种方法是用空白字符来分割字段

要实现这种方法,可将FS 设置为一个空格。在这种情况下,记录的前导空白字符和结尾空白字符 (空格和/或制表符) 将被忽略。并且字段空格和/或者制表位来分隔。因为FS 的默认值为一个空格,所以这也是通常情况下 awk 将记录划分为字段的方法。

  1. 第二种方法是使用其他单个字符来分隔字段。

当FS 表示任何单个字符时,在这个字符出现的任何地方都将分隔出另外一个字段。如果出现两个连续的分隔符,在它们之间的字段值为空串

  1. 第三种方法是如果设置了不止一个字符作为字段分隔符,那它将被作为一个正则表达式来解释。

例如

FS= “\t”
这个表示将每个制表符作为一个字段分隔符,
而:
FS=“\t+”
这个表示将每个制表符作为一个字段分割符

那如果要使用第二种方法,可以使用一个正则表达式指定几个字符作为分隔符

FS = "[:']"

## 这就表示用:以及'来作为分隔符

例如:

## 取出ip a 里面的ipv4地址
[root@kiwi222 ~]# ip a | grep 'inet' 
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host 
    inet 192.168.234.33/24 brd 192.168.234.255 scope global noprefixroute ens160
    inet6 fe80::20c:29ff:fe81:9835/64 scope link noprefixroute 


[root@kiwi222 ~]# ip a | grep 'inet' | grep -v '127' | awk -F'[ /]+' '{ print $3 }'
192.168.234.33
[root@kiwi222 ~]# 

## 上面这个例子中的awk -F'[ /]+' '{ print $3 }'用空格和/来作为分隔符,并匹配一次或者多次,然后打印第三个字符
5.表达式
5.1 转义序列
序列描述
\a报警字符,通常是ASCII BEL 字符
\b退格键
\f走纸符
\n换行符
\r回车
\t水平制表符
\v垂直制表符
\ddd将字符表示为1到3位八进制
\xbex将字符表示为十六进制值
\c任何需要字面表示的字符c(例如,“for”)
5.2 算数操作符
操作符描述
+
-
*
/
%取模
^取幂
**去幂

变量是引用值的标识符。定义变量只需要为它定义一个名字并将数据赋给它即
可。变量名只能由字母、数字和下划线组成。而且不能以数字开头。变量名的
大小写很重要: Salary 和 salary 是两个不同的变量,变量不必进行说明,你
不必告诉 awk 什么类型的数据存储在一个变量中。每个变量有一个字符串型值和数字型值,awk 能够根据表达式的前后关系来选择合适的值 (不包含数字的字符串值为 0)。变量不必初始化。awk 自动将它们初始化为空字符串,如果作为数字,它的值为 0。

列如:

[root@kiwi222 ~]# echo | awk 'BEGIN{x=4;y=x/4}{print y}' 
1
[root@kiwi222 ~]# 
5.3 统计空行数
[root@kiwi222 ~]# cat kiwi222 
kiwi123456

abc123

kiwi111:kiwi222

111:222
[root@kiwi222 ~]# 
[root@kiwi222 ~]# awk '/^$/ { print x += 1}' kiwi222 
1
2
3
[root@kiwi222 ~]# 

## 虽然这里没有为变量 x 赋初值。但在遇到第一个空行之前它的值一直为 0。表达式x+=1 在每次遇到空行时进行求值并将x 的值增加 1.print 语句打印表达式返回的值。因为我们在遇到每个空行时都执行 print 语句,所以我们得到了空行数的一个连续值。

还可以写成

++x 在返回结果前递增x的值 (前缀)
x++ 在返回结果后递增 x的值 (后缀)

“++”是递增操作符 (“”是递减操作符) 。表达式每计算一次变量的值就增加 1。递增和递减操作符可以出现在操作数的任何一边,与前缀或后缀操作符
样。位置不同可以得到不同的计算结果。

[root@kiwi222 ~]# awk '/^$/ { print x++ }' kiwi222 
0
1
2
[root@kiwi222 ~]# 

## 当遇到第一个空行时,表达式返回的值为“0“,遇到第二个空行时返回值为1,依此类推。如果将递增操作符放置与 x 的前面,当表达式第一次计算后,返回的值为“1”。
[root@kiwi222 ~]# awk '/^$/ { print ++x }' kiwi222 
1
2
3
[root@kiwi222 ~]#

## 当遇到第一个空行时返回的值为1。

统计空行数

[root@kiwi222 ~]# awk '/^$/ { ++x } END { print x }' kiwi222 
3
[root@kiwi222 ~]# 

## 也就是说等主题处理完之后再去结果(END)打印总数
5.4计算平均数

例如:下面有几组数字

[root@kiwi222 ~]# cat avg 
kiwi 55 66 87
jenny 78 89 54
xiaoming 44 88 74
[root@kiwi222 ~]# 

下面算出它们的平均数

[root@kiwi222 ~]# awk '{ total = $2+$3+$4;avg=total/3;print $1,avg }' avg 
kiwi 69.3333
jenny 73.6667
xiaoming 68.6667
[root@kiwi222 ~]#
6.系统变量

awk 中有许多系统变量或内置变量。awk 有两种类型的系统变量。第一种类型定义的变量默认值可以改变,例如默认的字段和记录分隔符。第二种类型定义的变量的值可用于报告或数据处理中。例如当前记录中字段的数量,当前记录的数量等。这些可以由 awk 自动更新,例如,当前记录的编号和输入文件名。

系统变量有以下几种:

  1. ARGC:命令行参数的数量。
  2. ARGV:一个数组,存储着命令行参数的值。
  3. FILENAME:当前正在处理的文件的名称。
  4. FNR:在当前文件中当前记录的记录号。
  5. NF:当前记录(行)中的字段数。
  6. NR:到目前为止读取的记录(行)数。
  7. OFS:输出字段分隔符。
  8. FS:输入字段分隔符。
  9. RS:输入记录分隔符。
  10. ORS:输出记录分隔符。
6.1 FS与OFS

FS:输入字段分隔符

OFS:输出字段分隔符

和FS等效的输出是OFS,它的默认值为一个空格。

例如:

[root@kiwi222 ~]# awk 'BEGIN{FS=" ";OFS=":"}{print $1,$3}' avg
kiwi:66
jenny:89
xiaoming:88
[root@kiwi222 ~]# 

[root@kiwi222 ~]# awk 'BEGIN{FS=" ";OFS=":"}{print $1 $3}' avg
kiwi66
jenny89
xiaoming88
[root@kiwi222 ~]# 

## 如果$1与$2中间为空格,意思就是不需要分隔符。也就是说想要使用分隔符的话逗号必须要加。

还可以为

[root@kiwi222 ~]# awk 'BEGIN{FS=" ";OFS="-"}{print $1,$3}' avg
kiwi-66
jenny-89
xiaoming-88
[root@kiwi222 ~]# 
6.2 NR

到目前为止读取的记录(行)数。

例如:

[root@kiwi222 ~]# ip a | grep 'inet' 
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host 
    inet 192.168.234.33/24 brd 192.168.234.255 scope global noprefixroute ens160
    inet6 fe80::20c:29ff:fe81:9835/64 scope link noprefixroute 
    
[root@kiwi222 ~]# ip a | grep 'inet' | awk -F'[ /]+' 'NR==3{ print $3 }'
192.168.234.33
[root@kiwi222 ~]# 

## 打印第三行的第三个字符
6.3 NF

NF:当前记录(行)中的字段数。

[root@kiwi222 ~]# cat kiwi333 
kiwi111 123456 //abc
kiwi111:123456://abc

[root@kiwi222 ~]# awk '{print NF}' kiwi333 
3
1
[root@kiwi222 ~]# 

提取最后一列

[root@kiwi222 ~]# awk '{print $NF}' kiwi333 
//abc
kiwi111:123456://abc
[root@kiwi222 ~]# 

提取倒数第二列

[root@kiwi222 ~]# awk '{print $(NF-1)}' kiwi333 
123456
kiwi111:123456://abc
[root@kiwi222 ~]# 
6.4 RS

RS:输入记录分隔符

ORS:输出记录分隔符

RS 输出等价的是 ORS,它的默认值也是一个换行符

7.处理多行记录

例如:

[root@kiwi222 ~]# df -h | awk 'NR==1{print $(NF-1),$NF}NR!=1{print $NF}'
Mounted on
/dev
/dev/shm
/run
/sys/fs/cgroup
/
/boot
/run/user/0
[root@kiwi222 ~]# 

列如:

[root@kiwi222 ~]# cat txt1 
kiwi 
jkjk
qweqwr
111
[root@kiwi222 ~]# 

## 还可以将字段分隔符定义为换行符

[root@kiwi222 ~]# awk 'BEGIN {FS="\n";RS=""}{print $1,$NF}' txt1
kiwi  111
[root@kiwi222 ~]# 
8.关系操作符与布尔操作符
8.1关系操作符
操作符描述
<小于
>大于
<=小于或等于
>=大于或等于
==相等的
!=不等的
~匹配
!-不匹配
[root@kiwi222 ~]# cat txt1 
kiwi 
jkjk
qweqwr
111

[root@kiwi222 ~]# awk 'NR==4 { print $0}' txt1
111
[root@kiwi222 ~]# 

## 只有第四行才会被打印
## 打印除了第一行的
[root@kiwi222 ~]# awk 'NR>1 { print $0}' txt1
jkjk
qweqwr
111
[root@kiwi222 ~]# 
8.2布尔操作符
操作符定义
||逻辑或
&&逻辑与
!逻辑非

给定两个或多个表达式,只有当给定的表达式之一的值为真 (非零或非空)时,使用操作符 的等个表达式的值才为真。而只有当&&操作符连接的两个表达式的值都为真时结果才为真。

操作符! 表示都对其值取反

!(NR > 1 && NF > 3)
9.获取文件的信息

处理命令ls的输出:

[root@kiwi222 ~]# ls -l $* | awk '{ print $5, "\t",$9}'
         
20       111
1183     anaconda-ks.cfg
47       avg
269      kiwi
98       kiwi111
45       kiwi222
42       kiwi333
324      list
113      nameState
394      newlist
98       sedscr
22       txt1
[root@kiwi222 ~]# 

## 打印文件的大小和名字。即打印第五个字段和第九个字段
## 打印每个文件的大小相加,得到列表的所有文件的总字节数
[root@kiwi222 ~]# ll | awk '{sum+=5;++filenum;print $5,"\t",$9}END{print "Total: ",sum}'
         
20       111
1183     anaconda-ks.cfg
47       avg
269      kiwi
98       kiwi111
45       kiwi222
42       kiwi333
324      list
113      nameState
394      newlist
98       sedscr
22       txt1
Total:  65
[root@kiwi222 ~]# 
10.格式化打印

awk提供的printf可以代替print语句,printf是借用了C程序设计语言,它和print不一样。printf语句和print语句一样可以打印一个简单的字符串。但print的结果会自动换行,但printf不会。

[root@kiwi222 ~]# awk 'BEGIN{printf "hello world"}'
hello world[root@kiwi222 ~]# ^C
[root@kiwi222 ~]# awk 'BEGIN{print "hello world"}'
hello world
[root@kiwi222 ~]# 

用在printf的格式说明符

字符定义
cASCII字符
d十进制整数
i十进制整数(在POSIX中增加的)
e浮点格式([-]d.precisione[±]dd)
E浮点格式([-]d.precisionE[±]dd)
f浮点格式 ([-]ddd.precision)
ge或f的转换形式,长度最短,末尾的0被去掉
GE或f的转换形式,长度最短,末尾的0被去掉
0无符号的八进制
s字符串
u无符号的十进制
x无符号的十六进制,用a-f表示10-15
X无符号的十六进制,用A-F表示10-15
%字面字符%

例如

[root@kiwi222 ~]# ll | awk 'NR!=1{print $5,"\t",$9}'
20       111
1183     anaconda-ks.cfg
47       avg
269      kiwi
98       kiwi111
45       kiwi222
42       kiwi333
324      list
113      nameState
394      newlist
98       sedscr
22       txt1
[root@kiwi222 ~]# ll | awk 'NR!=1{printf "%d\t%s\n", $5,$9}'
20      111
1183    anaconda-ks.cfg
47      avg
269     kiwi
98      kiwi111
45      kiwi222
42      kiwi333
324     list
113     nameState
394     newlist
98      sedscr
22      txt1
[root@kiwi222 ~]# 

## %d表示字符串,%s表示数字
## 该语句输出$5 的值,后面是制表符\t 和$9、然后输出一个换行符。

将ls命令左对齐,并且算出总数

[root@kiwi222 ~]# ll | awk 'BEGIN{print "bytes","\t","file"}NR !=1{sum+=$5;++filenum;print $5,"\t",$9}END{printf "Total:\t %d bytes(%d file)\n",sum,filenum}'
bytes    file
20       111
1183     anaconda-ks.cfg
47       avg
269      kiwi
98       kiwi111
45       kiwi222
42       kiwi333
324      list
113      nameState
394      newlist
98       sedscr
22       txt1
Total:   2655 bytes(12 file)
[root@kiwi222 ~]# 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值