Awk 简介
- Awk 是被设计用于文本处理,并通常被用作数据提取和报告工具的解释性程序设计语言
- Awk 分别代表其三个作者姓氏的第一个字母: Alfred Aho 、Peter Weinberger、Brain Kernighan
- 目前在Linux中常用的是 awk 编译版本有 mawk 、gawk
- 以 RedHat 为代表使用的是 gawk,以Ubuntu为代表使用的是 mawk
- gawk 是GUN Project 的 awk解释器的开源代码实现
- CentOS 当然也用的是gawk
Awk 工作流程
- 按行扫描文件,当某行匹配一个模式时,awk 就在那一行上执行指定的操作。awk 持续地用这种方式处理输入的行,直到处理到输入文件的结尾为止
Awk 的两个特殊模式
BEGIN 和 END,BEGIN 被放置在没有读取任何数据之前,而 END 被放置在所有的数据读取完成以后执行
体现如下:
- BEGIN{ 读入第一行文本之前执行,一般用来初始化操作 }
- { 逐行处理,逐行读入文本执行相应的处理,是最常见的编辑指令 }:
- END{ 处理完最后一行文本之后执行,一般用来输出处理结果 }
Awk 语法
awk [ 选项 ] -f program-file [- -] file
选项如下:
(1)指定以 fs 作为输入行的分隔符(默认分隔符为空格或值表符)
-F fs --field-separator=fs
(2)从脚本文件中读取 Awk 指令,以取代在命令参数中输入处理脚本
-f progfile --file=progfile
(3)在执行处理过程以前,设置一个变量 var 值为val
-v var=val --assign=var=val
(4)使用兼容模式运行 Awk ,GUN 扩展选项将被忽略
-W compat , -W traditional , --compat ,--tradional
(5)打印全局变量(变量名、类型、值)到文件中,如果没有提供文件名,则自动输出至名为 dump-variables 的文件中
-W dump-variables[=file], --dump-variables[=file]
(6)输出简短的GNU版权信息
-W copyleft, -W copyright,
Awk 语法结构
一个 awk 程序包含一系列的模式 { 动作指令 } 或是函数定义,模式可以是 BEGIN 、END 、表达式,用来限定操作对象的多个表达式使用分隔;动作指令需要以 { } 引起来。
示例一
(1)通过正则表达式 /^$/ 匹配空白行,动作为打印 Blank line,即文件有 N 个空白行,则打印出 N 个 Blank line
[root@test ~]# cat test.txt
hello world
hello sed
hello awk
[root@test ~]# awk '/^$/ {print "Blank line"}' test.txt
Blank line
Blank line
[root@test ~]#
(2)打印包含 IPADDR 的行,没有指定动作指令,默认动作为打印
[root@test ~]# awk '/IPADDR/' /etc/sysconfig/network-scripts/ifcfg-eth0
IPADDR=172.25.20.100
[root@test ~]#
(3)提前编辑一个 awk 脚本,再通过 -f 选项调用该脚本
[root@test ~]# cat test.txt
hello world
hello sed
hello awk
[root@test ~]# cat awk.sh #编辑脚本
/^$/ {print "Blank line"}
[root@test ~]# awk -f awk.sh test.txt #调用脚本
Blank line
Blank line
[root@test ~]#
Awk 操作指令
1、记录与字段
- Awk 一次从文件中读取一条记录,并将记录存储在字段变量 $0 中。
- 记录被分割为字段并存储在 $1、$2 、$3、… 、$NF 中(默认使用空格或制表符为分隔符)。
- 内建变量 NF 为记录的字段个数。
示例二
(1)读取输入行并输出第一个字段、第二个字段、第三个字段。
[root@test ~]# echo hello awk world | awk '{print $1 $2 $3}'
helloawkworld
[root@test ~]#
(2)读取输入行并输出该行
[root@test ~]# echo hello awk world | awk '{print $0}'
hello awk world
[root@test ~]#
(3)读取输入行并输出该行的字段个数
[root@test ~]# echo hello awk world | awk '{print NF}'
3
[root@test ~]#
(4)读取输入行并输出该行最后一个字段
[root@test ~]# echo hello awk world | awk '{print $NF}'
world
[root@test ~]#
2、字段分隔符
- 默认 Awk 读取数据以空格或制表符作为分隔符
- 可以通过 -F 或 FS ( field separator ) 变量来改变分隔符
示例三
(1)以冒号为分隔符打印 passwd 文件的第一个字段,用 -F 实现
[root@test ~]# awk -F: '{print $1}' /etc/passwd
(2)以冒号为分隔符打印 passwd 文件的第一个字段,用 FS 实现
[root@test ~]# awk 'BEGIN {FS = ":"} {print $1}' /etc/passwd
如果使用 FS 改变分隔符,需要在 BEGIN 处定义 FS ,因为在读取第一行前,就需要改变字段分隔符
(3)指定多个字段分隔符 ( FS )
[root@test ~]# echo 'hello awk:world,!' | awk 'BEGIN {FS="[:,]"} {print $1,$2,$3,$4}'
hello awk world !
[root@test ~]#
3、内置变量
变量名称 | 描 述 |
---|---|
ARGC | 命令行参数的个数 |
FILENAME | 当前输入文档的名称 |
FNR | 当前输入文档的当前记录编号,尤其是当有多个输入文档时有用 |
NR | 输入流的当前记录编号 |
NF | 当前记录的字段个数 |
FS | 字段分隔符 |
OFS | 输出字段分隔符,默认为空格 |
ORS | 输出记录分隔符,默认为换行符 \n |
RS | 输入记录分隔符,默认为换行符 \n |
示例四
(1)前期准备,编辑测试文档,test1.txt 与 test2.txt
[root@test ~]# cat test1.txt #test1.txt
This is a test file.
Do yourself!
[root@test ~]# cat test2.txt #test2.txt
Hello the awk.
Congratulations on your success!
Five minus two is three.
[root@test ~]#
(2)输出 当前文档 的 当前行 编号,第一个文件两行,第二个文件三行(FNR)
[root@test ~]# awk '{print FNR}' test1.txt test2.txt
1
2
1
2
3
[root@test ~]#
(3)Awk 将两个文档作为一个 整体 的输入流,通过 NR 输入当前编号(NR)
[root@test ~]# awk '{print NR}' test1.txt test2.txt
1
2
3
4
5
[root@test ~]#
(4)输出 当前文档 的 每行字段 的 个数(NF)
[root@test ~]# awk '{print NF}' test1.txt test2.txt
5
2
3
4
5
[root@test ~]#
(5)输出文档的前三个字段(默认以 空格 为分隔符)(FS)
[root@test ~]# cat test1.txt
This is a test file.
Do yourself!
[root@test ~]# awk '{print $1 $2 $3}' test1.txt
Thisisa
Doyourself!
[root@test ~]# awk '{print $1,$2,$3}' test1.txt
This is a
Do yourself!
[root@test ~]# awk 'BEGIN {FS= ":"} {print $1}' /etc/passwd #分隔符改为冒号
(6)将输出分隔符设置为 “-” ,即 print 在输出第一、二、三个字段时,中间的分隔符是 “-”
[root@test ~]# awk 'BEGIN {OFS="-"} {print $1,$2,$3}' test2.txt
Hello-the-awk.
Congratulations-on-your
Five-minus-two
[root@test ~]#
(7)读取输入数据,以空白行为记录分隔符,即第一个空白行前的内容为第一个记录,第一个记录中字段分隔符为换行符(打印邮件时间,即每个记录的第三个字段)
[root@test ~]# cat test3.txt
mail from:linux@xuptip.com
subject:hello
data:2018-12-18 12:19
content:Hello,best wishes for you!
mail from:redhat@xuptip.com
subject:Test
data:2018-12-18 03:04
content:This is a test mail.
mail from:centos@xuptip.com
subject:Congratulations
data:2018-12-18 08:02
content:Congratulations on your success!
[root@test ~]# awk 'BEGIN {FS="\n";RS=""} {print $3}' test3.txt
data:2018-12-18 12:19
data:2018-12-18 03:04
data:2018-12-18 08:02
[root@test ~]#
4、表达式与操作符
- 表达式由变量、常量、函数、正则表达式、操作符组成
- Awk 中变量有字符变量和数字变量
- 如果在 Awk 中定义的变量没有初始化,则初始值为空字符串或0
- 字符串操作需要加引号
#变量定义如下:
a="hello world"
b=12
操作符 | 描述 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 取余 |
^ | 幂运算 |
++ | 自加 1 |
– | 自减 1 |
+= | 相加后赋值给变量( x+=9 等同于 x=x+9 ) |
-= | 相减后赋值给变量( x-=9 等同于 x=x-9 ) |
*= | 相乘后赋值给变量( x*=9 等同于 x=x*9 ) |
/= | 相除后赋值给变量( x/=9 等同于 x=x/9 ) |
> | 大于 |
< | 小于 |
>= | 大于或等于 |
<= | 小于或等于 |
== | 等于 |
!= | 不等于 |
~ | 匹配 |
!~ | 不匹配 |
&& | 与 |
|| | 或 |
示例五
(1)实现单个运算的简单加法
[root@test ~]# echo "test" | awk 'x=2 {print x+3}'
5
[root@test ~]#
(2)实现多个运算的混合算法
[root@test ~]# echo "test" | awk 'x=2,y=3 {print (x+3)/5,y*6}'
1 18
[root@test ~]#
(3)统计所有的空白行
[root@test ~]# cat test.txt
hello world
hello sed
hello awk
[root@test ~]# awk '/^$/ {print x+=1}' test.txt
1
2
[root@test ~]#
(4)打印总的空白行的行数
[root@test ~]# cat test.txt
hello world
hello sed
hello awk
[root@test ~]# awk '/^$/ {x+=1} END {print x}' test.txt
2
[root@test ~]#
(5)打印 root 的 ID 号
[root@test ~]# awk -F : '$1~/root/ {print $3}' /etc/passwd
0
[root@test ~]#
(6)找出系统中 ID 号大于 100 的用户名
[root@test ~]# awk -F: '$3>100 {print $1}' /etc/passwd
avahi-autoipd
systemd-bus-proxy
systemd-network
polkitd
student
[root@test ~]#
打印九九乘法表
[root@test ~]# seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"\n":"\t")}'
1x1=1
1x2=2 2x2=4
1x3=3 2x3=6 3x3=9
1x4=4 2x4=8 3x4=12 4x4=16
1x5=5 2x5=10 3x5=15 4x5=20 5x5=25
1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36
1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49
1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64
1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81