三剑客之Awk基础用法与工作原理

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

猜你喜欢

转载自blog.csdn.net/sunny_future/article/details/80201133