文件处理三大利器之三:awk
目录
前言
1. 概述
2. 工作原理
3. 基本语法
4. 内置变量
5.演示
总结
前言
在文件处理的实用工具体系中,我们已先后探讨了灵活高效的命令行小工具,以及擅长文本流编辑的 sed。接下来,让我们将焦点转向另一个功能强大的核心工具 ——awk,深入了解它在复杂文本处理场景中的独特优势与实用技巧。
1. 概述
AWK 是一种处理文本文件的语言,是强大的文本分析工具。它专门为文本处理设计,通常用于扫描、过滤、统计汇总工作。AWK 得名于其三位创始人 Alfred Aho、Peter Weinberger 和 Brian Kernighan 的姓氏首字母。
在 GNU/Linux 系统中,通常使用的是 GNU AWK(gawk),awk
命令通常是gawk
的符号链接。
2. 工作原理
awk 的工作原理可以概括为逐行处理:
-
从文件、管道或标准输入中逐行读取文本。
-
默认以空格或制表符为分隔符将行分割成多个字段。
-
将分隔所得的各个字段保存到内建变量中(如$1、$2 等)。
-
按模式或者条件执行编辑命令。
-
重复上述过程直到文件结束。
与 sed 常用于整行处理不同,awk 更倾向于将一行分成多个字段后再进行处理。
3. 基本语法
命令格式:
awk [options] '模式或条件{操作}' 文件1 文件2 ...
awk -f 脚本文件 文件1 文件2 ...
常用选项:
-
-F
:指定字段分隔符 -
-v
:定义变量 -
-f
:指定脚本文件
核心结构:
awk 'BEGIN{ 初始化操作 } 模式{ 处理动作 } END{ 汇总操作 }' 文件
-
BEGIN
:处理文件前执行(只执行一次) -
模式:决定动作的触发条件
-
END
:处理完所有行后执行(只执行一次)
4. 内置变量
awk 提供了许多内置变量,常用的有:
-
FS
:输入字段分隔符(默认空格或制表符) -
NF
:当前行的字段个数(列数) -
NR
:当前处理的行号(序数) -
$0
:当前行的整行内容 -
$n
:当前行的第 n 个字段 -
FILENAME
:当前处理的文件名 -
OFS
:输出字段分隔符(默认空格)
5.演示
1 #!/bin/bash #原脚本cc2 lisa3:x:707:1007::/home/lisa3:/bin/bash3 webmaster:x:1005:10::/home/admin:/bin/bash4 mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false5 apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin6 named:x:25:25:Named:/var/named:/sbin/nologin7 zc:x:1006:1006::/home/zc:/bin/bash8 zc2:x:1007:1008::/home/zc2:/bin/bash9 yang:x:1008:1009::/home/yang:/bin/bash10 vivi:x:1009:1010::/home/vivi:/bin/bash
[root@110 function]# cat fun10.sh
#!/bin/bash
lisa3:x:707:1007::/home/lisa3:/bin/bash
webmaster:x:1005:10::/home/admin:/bin/bash
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
named:x:25:25:Named:/var/named:/sbin/nologin
zc:x:1006:1006::/home/zc:/bin/bash
zc2:x:1007:1008::/home/zc2:/bin/bash
yang:x:1008:1009::/home/yang:/bin/bash
vivi:x:1009:1010::/home/vivi:/bin/bash
pi:x:1010:1011::/home/pi:/bin/bash
zc2:x:1007:1008::/home/zc2:/bin/bash
yang:x:1008:1009::/home/yang:/bin/bash
vivi:x:1009:1010::/home/vivi:/bin/bash
pi:x:1010:1011::/home/pi:/bin/bash[root@110 function]# awk '{print "yang"}' < fun10.sh #把fun10.sh的每一行都输出为yang
yang
yang
yang
yang
yang
yang
yang
yang
yang
yang
yang
yang
yang
yang
yang
yang[root@110 function]# cat fun10.sh | head -10 > cc #把fun10.sh的前十行输入新文档cc[root@110 function]# awk '{print}' cc #把cc的内容全部打印出
#!/bin/bash
lisa3:x:707:1007::/home/lisa3:/bin/bash
webmaster:x:1005:10::/home/admin:/bin/bash
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
named:x:25:25:Named:/var/named:/sbin/nologin
zc:x:1006:1006::/home/zc:/bin/bash
zc2:x:1007:1008::/home/zc2:/bin/bash
yang:x:1008:1009::/home/yang:/bin/bash
vivi:x:1009:1010::/home/vivi:/bin/bash[root@110 function]# awk '{print $1}' cc #以默认空格为分隔符,打印出cc的第一列
lisa3:x:707:1007::/home/lisa3:/bin/bash
webmaster:x:1005:10::/home/admin:/bin/bash
mysql:x:27:27:MySQL
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
named:x:25:25:Named:/var/named:/sbin/nologin
zc:x:1006:1006::/home/zc:/bin/bash
zc2:x:1007:1008::/home/zc2:/bin/bash
yang:x:1008:1009::/home/yang:/bin/bash
vivi:x:1009:1010::/home/vivi:/bin/bash[root@110 function]# awk -F: '{print $1}' cc #以:为分隔符,打印出cc的第一列
#!/bin/bash
lisa3
webmaster
mysql
apache
named
zc
zc2
yang
vivi[root@110 function]# awk -F: '{print $5}' cc #以:为分隔符,打印出cc的第五列MySQL Server
Apache
Named[root@110 function]# cat zz1
hello hello[root@110 function]# awk '{print $1 $2}' zz1 #输出zz1的第1、2列,默认分隔符为空格
hellohello[root@110 function]# awk '{print $1 $2}' zz1 #可以看出$1、$2之间空格多少都无所谓
hellohello[root@110 function]# awk '{print $1" "$2}' zz1 #输出zz1的第1、2列,以空格隔开,双引号之间要加空格;默认分隔符为空格
hello hello[root@110 function]# awk '{print $1,$2}' zz1 #输出zz1的第1、2列,以空格隔开,效果同上
hello hello[root@110 function]# awk '{print $1"\t"$2}' zz1 #输出zz1的第1、2列,以制表符\t隔开,即空四格
hello hello[root@110 function]# awk -F[:/] '{print $3}' cc #以:和/为分隔符,输出每行都第三行
bash
707
1005
27
48
25
1006
1007
1008
1009
# 内建变量:
# $1: 第一行
# $2:第二行
# $0:代表整行
# NF:一行的列数
# NR:行数-----------------------------------------------------------------------------------------[root@110 function]# awk -F: '/lisa/{print $0}' cc #打印包含lisa的整行内容
lisa3:x:707:1007::/home/lisa3:/bin/bash[root@110 function]# awk -F: '/lisa/{print $1}' cc #打印包含lisa的行的第一列
lisa3
[root@110 function]# awk -F: '/lisa/{print $1,$6}' cc #打印包含lisa的行的第一、六行
lisa3 /home/lisa3[root@110 function]# awk '/lisa/' cc #打印包含lisa的行内容
lisa3:x:707:1007::/home/lisa3:/bin/bash[root@110 function]# awk -F[:/] '{print NF}' cc #以:/为分隔符,输出每一行有多少列
3
11
11
12
12
11
11
11
11
11[root@110 function]# awk -F[:/] '{print NR}' cc #显示行号
1
2
3
4
5
6
7
8
9
10[root@110 function]# awk -F: '{print NR,$0}' cc #输出每一行,前面带行号,用空格隔开
1 #!/bin/bash
2 lisa3:x:707:1007::/home/lisa3:/bin/bash
3 webmaster:x:1005:10::/home/admin:/bin/bash
4 mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
5 apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
6 named:x:25:25:Named:/var/named:/sbin/nologin
7 zc:x:1006:1006::/home/zc:/bin/bash
8 zc2:x:1007:1008::/home/zc2:/bin/bash
9 yang:x:1008:1009::/home/yang:/bin/bash
10 vivi:x:1009:1010::/home/vivi:/bin/bash[root@110 function]# awk 'NR==2' fun10.sh #输出行号为2的一整行
lisa3:x:707:1007::/home/lisa3:/bin/bash[root@110 function]# awk 'NR==2{print}' fun10.sh #输出行号为2的一整行
lisa3:x:707:1007::/home/lisa3:/bin/bash[root@110 function]# awk 'NR==2{print $1}' fun10.sh #输出行号为2的行的第一列,默认分隔符为空格
lisa3:x:707:1007::/home/lisa3:/bin/bash[root@110 function]# awk -F: 'NR==2{print $1}' fun10.sh #输出行号为2的行的第一列,分隔符为:
lisa3[root@110 function]# awk -F: '{print $NF}' fun10.sh #分隔符为:,输出每行的最后一列
#!/bin/bash
/bin/bash
/bin/bash
/bin/false
/sbin/nologin
/sbin/nologin
/bin/bash
/bin/bash
/bin/bash
/bin/bash
/bin/bash
/bin/bash
/bin/bash
/bin/bash
/bin/bash[root@110 function]# awk 'END{print NR}' fun10.sh 输出最后一行的行号(总行号)
16[root@110 function]# awk 'END{print $0}' cc #输出最后一行
vivi:x:1009:1010::/home/vivi:/bin/bash[root@110 function]# awk -F: '{print "第"NR"行有"NF"列"}' cc #分隔符设为:,每一行输出第几行有几列
第1行有1列
第2行有7列
第3行有7列
第4行有7列
第5行有7列
第6行有7列
第7行有7列
第8行有7列
第9行有7列
第10行有7列
# 生产环境中的使用:[root@110 function]# ifconfig ens33 # 查看本机ens33端口
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 192.168.10.110 netmask 255.255.255.0 broadcast 192.168.10.255inet6 fe80::28c8:c4e3:2a8b:51d9 prefixlen 64 scopeid 0x20<link>ether 00:0c:29:5d:9d:28 txqueuelen 1000 (Ethernet)RX packets 10669 bytes 1000590 (977.1 KiB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 6286 bytes 629701 (614.9 KiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0[root@110 function]# ifconfig ens33 | awk '/netmask/{print "本机的ip地址为"$2}'
本机的ip地址为192.168.10.110[root@110 function]# ifconfig ens33 | awk '/RX p/{print $5"字节"}'
1024572字节
------------------------------------------------------------------------------------------
[root@110 function]# df -h #查看磁盘分区
文件系统 容量 已用 可用 已用% 挂载点
devtmpfs 895M 0 895M 0% /dev
tmpfs 910M 0 910M 0% /dev/shm
tmpfs 910M 10M 900M 2% /run
tmpfs 910M 0 910M 0% /sys/fs/cgroup
/dev/sda3 96G 6.3G 90G 7% /
/dev/sda1 497M 174M 324M 35% /boot
tmpfs 182M 0 182M 0% /run/user/0[root@110 function]# df -h | awk 'NR==2{print $4}'
895M
# awk 的运算
# BEGIN一般用来做初始化操作,仅在读取数据记录之前执行一次
# END一般用作汇总操作,仅在读取完数据记录之后执行一次
-----------------------------------------------------------------------------------------
[root@110 function]# awk 'BEGIN{x=10;print x}'
10[root@110 function]# awk 'BEGIN{x=10;print x+1}'
11[root@110 function]# awk 'BEGIN{x=10;x++;print x}'
11[root@110 function]# awk 'BEGIN{print x+1}'
1[root@110 function]# awk 'BEGIN{print 2.5+3.5}'
6[root@110 function]# awk 'BEGIN{print 2-1}'
1[root@110 function]# awk 'BEGIN{print 3*4}'
12[root@110 function]# awk 'BEGIN{print 3**2}'
9[root@110 function]# awk 'BEGIN{print 3^2}'
9[root@110 function]# awk 'BEGIN{print 1/2}'
0.5
# 模糊匹配: ~表示包含 !~表示不包含
------------------------------------------------------------------------------------------[root@110 function]# awk -F: '/lisa/' cc #包含lisa的行
lisa3:x:707:1007::/home/lisa3:/bin/bash[root@110 function]# awk -F: '$1~/lisa/' cc #输出第一列包含lisa的整行
lisa3:x:707:1007::/home/lisa3:/bin/bash[root@110 function]# awk -F: '$1~/li/' cc #输出第一列包含li的整行
lisa3:x:707:1007::/home/lisa3:/bin/bash[root@110 function]# awk -F: '$7!~/nologin$/{print $1,$7}' cc #输出第7列以nologin结尾的整行
#!/bin/bash
lisa3 /bin/bash
webmaster /bin/bash
mysql /bin/false
zc /bin/bash
zc2 /bin/bash
yang /bin/bash
vivi /bin/bash
# 数值与字符串的比较:
== 等于
!= 不等于
<= 小于等于
>= 大于等于
> 大于
< 小于------------------------------------------------------------------------------------------[root@110 function]# awk 'NR==5{print}' cc #输出第五行
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin[root@110 function]# awk 'NR==5' cc #输出第五行
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin[root@110 function]# awk 'NR<5' cc #输出行号小于5的行
#!/bin/bash
lisa3:x:707:1007::/home/lisa3:/bin/bash
webmaster:x:1005:10::/home/admin:/bin/bash
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false[root@110 function]# awk -F: '$3==707' cc #输出第五列为707的行
lisa3:x:707:1007::/home/lisa3:/bin/bash[root@110 function]# awk -F: '$1==lisa' cc
[root@110 function]# awk -F: '$1==lisa3' cc
[root@110 function]# awk -F: '$1=="lisa3"' cc #精确匹配,输出第一列为lisa3的行
lisa3:x:707:1007::/home/lisa3:/bin/bash[root@110 function]# awk -F: '$3>1000' cc #输出第3列数值大于1000的行
webmaster:x:1005:10::/home/admin:/bin/bash
zc:x:1006:1006::/home/zc:/bin/bash
zc2:x:1007:1008::/home/zc2:/bin/bash
yang:x:1008:1009::/home/yang:/bin/bash
vivi:x:1009:1010::/home/vivi:/bin/bash
# 逻辑运算:
# && ||-----------------------------------------------------------------------------------------[root@110 function]# nl cc1 #!/bin/bash2 lisa3:x:707:1007::/home/lisa3:/bin/bash3 webmaster:x:1005:10::/home/admin:/bin/bash4 mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false5 apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin6 named:x:25:25:Named:/var/named:/sbin/nologin7 zc:x:1006:1006::/home/zc:/bin/bash8 zc2:x:1007:1008::/home/zc2:/bin/bash9 yang:x:1008:1009::/home/yang:/bin/bash10 vivi:x:1009:1010::/home/vivi:/bin/bash[root@110 function]# awk -F: '$3<10 || $3>=1000' cc
#!/bin/bash
webmaster:x:1005:10::/home/admin:/bin/bash
zc:x:1006:1006::/home/zc:/bin/bash
zc2:x:1007:1008::/home/zc2:/bin/bash
yang:x:1008:1009::/home/yang:/bin/bash
vivi:x:1009:1010::/home/vivi:/bin/bash[root@110 function]# awk -F: '$3>10 && $3<1000' cc
lisa3:x:707:1007::/home/lisa3:/bin/bash
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
named:x:25:25:Named:/var/named:/sbin/nologin[root@110 function]# awk -F: '$3>4 && $3<40' cc
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
named:x:25:25:Named:/var/named:/sbin/nologin
# 其他内置变量用法:# FS:输入字符的分隔符,默认为空格
# OFS:输出字符的分隔符,默认为空格
# FNR:读取文件的记录数(行号),从1开始,新的文件重新从1开始
# RS:输入行分隔符,默认为换行符
# ORS:输出行分隔符,默认为换行符------------------------------------------------------------------------------------------
[root@110 function]# nl cc #显示行号1 #!/bin/bash2 lisa3:x:707:1007::/home/lisa3:/bin/bash3 webmaster:x:1005:10::/home/admin:/bin/bash4 mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false5 apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin6 named:x:25:25:Named:/var/named:/sbin/nologin7 zc:x:1006:1006::/home/zc:/bin/bash8 zc2:x:1007:1008::/home/zc2:/bin/bash9 yang:x:1008:1009::/home/yang:/bin/bash10 vivi:x:1009:1010::/home/vivi:/bin/bash[root@110 function]# nl zz1 #显示行号1 hello hello------------------------------------------------------------------------------------------[root@110 function]# awk 'BEGIN{FS=":"}{print $1}' cc #以:为分隔符,输出每行第一列
#!/bin/bash
lisa3
webmaster
mysql
apache
named
zc
zc2
yang
vivi[root@110 function]# awk 'BEGIN{FS=":";OFS="---"}{print $1,$2}' cc #以:为分隔符,输出每行第1、2列,并用---隔开
#!/bin/bash---
lisa3---x
webmaster---x
mysql---x
apache---x
named---x
zc---x
zc2---x
yang---x
vivi---x[root@110 function]# awk '{print FNR,$0}' cc #输出每一行,开头带行号
1 #!/bin/bash
2 lisa3:x:707:1007::/home/lisa3:/bin/bash
3 webmaster:x:1005:10::/home/admin:/bin/bash
4 mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
5 apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
6 named:x:25:25:Named:/var/named:/sbin/nologin
7 zc:x:1006:1006::/home/zc:/bin/bash
8 zc2:x:1007:1008::/home/zc2:/bin/bash
9 yang:x:1008:1009::/home/yang:/bin/bash
10 vivi:x:1009:1010::/home/vivi:/bin/bash[root@110 function]# awk '{print FNR,$0}' cc zz1 #输出每一行,开头带行号,新文件从1开始
1 #!/bin/bash
2 lisa3:x:707:1007::/home/lisa3:/bin/bash
3 webmaster:x:1005:10::/home/admin:/bin/bash
4 mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
5 apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
6 named:x:25:25:Named:/var/named:/sbin/nologin
7 zc:x:1006:1006::/home/zc:/bin/bash
8 zc2:x:1007:1008::/home/zc2:/bin/bash
9 yang:x:1008:1009::/home/yang:/bin/bash
10 vivi:x:1009:1010::/home/vivi:/bin/bash
1 hello hello[root@110 function]# awk '{print NR,$0}' cc zz1 #输出每一行,开头带行号
1 #!/bin/bash
2 lisa3:x:707:1007::/home/lisa3:/bin/bash
3 webmaster:x:1005:10::/home/admin:/bin/bash
4 mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
5 apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
6 named:x:25:25:Named:/var/named:/sbin/nologin
7 zc:x:1006:1006::/home/zc:/bin/bash
8 zc2:x:1007:1008::/home/zc2:/bin/bash
9 yang:x:1008:1009::/home/yang:/bin/bash
10 vivi:x:1009:1010::/home/vivi:/bin/bash
11 hello hello[root@110 function]# awk 'BEGIN{RS=":"}{print $0}' cc #以:为分隔符,输出每一行
#!/bin/bash
lisa3
x
707
1007/home/lisa3
/bin/bash
webmaster
x
1005
10/home/admin
/bin/bash
mysql
x
27
27
MySQL Server
/var/lib/mysql
/bin/false
apache
x
48
48
Apache
/usr/share/httpd
/sbin/nologin
named
x
25
25
Named
/var/named
/sbin/nologin
zc
x
1006
1006/home/zc
/bin/bash
zc2
x
1007
1008/home/zc2
/bin/bash
yang
x
1008
1009/home/yang
/bin/bash
vivi
x
1009
1010/home/vivi
/bin/bash[root@110 function]# awk 'BEGIN{RS=" "}{print $0}' zz1 #以空格为分隔符,输出每一行
hello
hello[root@110 function]# awk 'BEGIN{ORS=" "}{print $0}' cc #多行合并为一行输出,输出时自定义以空格为分隔符分割每行,本来默认的是回车
#!/bin/bash lisa3:x:707:1007::/home/lisa3:/bin/bash webmaster:x:1005:10::/home/admin:/bin/bash mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin named:x:25:25:Named:/var/named:/sbin/nologin zc:x:1006:1006::/home/zc:/bin/bash zc2:x:1007:1008::/home/zc2:/bin/bash yang:x:1008:1009::/home/yang:/bin/bash vivi:x:1009:1010::/home/vivi:/bin
总结
awk是强大的处理文本文件的语言和文本分析工具,用于扫描、过滤、统计汇总工作
原理:在开始(begin)模块通过AWK开始模块读取数据,在工作(pattern)模块执行命令,有时会在前两个模块循环,处理完毕,开始执行(END)结束模块的命令。即读、执行、重复
语法:
awk 选项 模式/条件 文件1 文件2 ...
FS: 每行字段分隔符 默认空格
NF: 列的个数
NR: 处理行的行号
$0: 整行
$n: 第n列
FILENAME: 被处理的文件名
FNR: 各文件分别计数的行号 默认空格
OFS: 输出字段分隔符 默认空格
ORS: 输出记录分隔符 默认换行符
RS: 行分隔符