NoteDeep
awk 简介

awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。
awk 是一种很棒的语言,它适合文本处理和报表生成,其语法较为常见,借鉴了某些语言的一些精华,如 C 语言等。在 linux 系统日常处理工作中,发挥很重要的作用,掌握了 awk将会使你的工作变的高大上。 awk 是三剑客的老大,利剑出鞘,必会不同凡响。


使用方法

awk '{pattern + action}' {filenames}
尽管操作可能会很复杂,但语法总是这样,其中 pattern 表示 AWK 在数据中查找的内容,而 action 是在找到匹配内容时所执行的一系列命令。花括号({})不需要在程序中始终出现,但它们用于根据特定的模式对一系列指令进行分组。 pattern就是要表示的正则表达式,用斜杠括起来。
awk语言的最基本功能是在文件或者字符串中基于指定规则浏览和抽取信息,awk抽取信息后,才能进行其他文本操作。完整的awk脚本通常用来格式化文本文件中的信息。
通常,awk是以文件的一行为处理单位的。awk每接收文件的一行,然后执行相应的命令,来处理文本。


AWK 工作原理
AWK 工作流程可分为三个部分:
  • 读输入文件之前执行的代码段(由BEGIN关键字标识)。
  • 主循环执行输入文件的代码段。
  • 读输入文件之后的代码段(由END关键字标识)。
命令结构:
awk 'BEGIN{ commands } pattern{ commands } END{ commands }'

下面的流程图描述了AWK的工作流程:
1、通过关键字 BEGIN 执行 BEGIN 块的内容,即 BEGIN 后花括号 {} 的内容。
2、完成 BEGIN 块的执行,开始执行body块。
3、读入有 \n 换行符分割的记录。
4、将记录按指定的域分隔符划分域,填充域,$0 则表示所有域(即一行内容),$1 表示第一个域,$n 表示第 n 个域。
5、依次执行各 BODY 块,pattern 部分匹配该行内容成功后,才会执行 awk-commands 的内容。
6、循环读取并执行各行直到文件结束,完成body块执行。
7、开始 END 块执行,END 块可以输出最终结果。

BEGIN和END块
通常,对于每个输入行, awk 都会执行每个脚本代码块一次。然而,在许多编程情况中,可能需要在 awk 开始处理输入文件中的文本之前执行初始化代码。对于这种情况, awk 允许您定义一个 BEGIN 块。 其语法格式: BEGIN {awk-commands}
开始块就是在程序启动的时候执行的代码部分,并且它在整个过程中只执行一次。
因为 awk 在开始处理输入文件之前会执行 BEGIN 块,因此它是初始化 FS(字段分隔符)变量、打印页眉或初始化其它在程序中以后会引用的全局变量的极佳位置。
BEGIN是AWK的关键字,因此必须是大写的。开始块部分是可选的。
awk 还提供了另一个特殊块,叫作 END 块。 awk 在处理了输入文件中的所有行之后执行这个块。通常, END 块用于执行最终计算或打印应该出现在输出流结尾的摘要信息。其语法格式: END {awk-commands}
END是AWK的关键字,因此也必须大写。结束块也是可选的。

主体块(BODY)
主体部分的语法格式如下:
/pattern/ {awk-commands}
对于每一个输入的行都会执行一次主体部分的命令。
默认情况下,对于输入的每一行,AWK 都会执行命令。但是,我们可以将其限定在指定的模式中。
注意:在主体块部分没有关键字存在。

下面通过示例来演示一遍
示例一:统计/etc/passwd的帐户人数
[root@localhost135 tmp]# awk '{count++;print $0} END{print "user count is", count}' /etc/passwd # print $0 表示打印整行信息,具体关于$0用法后面会讲解
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
.......................................
user count is 53
[root@localhost135 tmp]# awk '{count++} END{print "user count is", count}' /etc/passwd # 取消 print $0 只打印出帐户人数,不打印具体信息
user count is 53
[root@localhost135 tmp]# awk 'BEGIN {count=0;print "[start] user count is ",count} {count=count+1} END{print "usercount is ", count}' /etc/passwd
[start] user count is 0
user count is 53
[root@localhost135 tmp]#

示例二:统计某个文件夹下的文件占用的字节数
[root@localhost135 tmp]# ll | awk 'BEGIN{size=0} {size=size+$5} END{print "The total size is ",size}' # 统计 /etc/tmp 文件夹下的文件大小总和
The total size is 5432
[root@localhost135 tmp]# ll | awk 'BEGIN{size=0} {size=size+$5;print $0} END{print "The total size is ",size/1024/1024}' # 以M为单位
总用量 12
-rw-r--r--. 1 root root 39 5月 16 10:22 awktest
-rw-r--r--. 1 root root 2638 2月 23 14:55 passwd
drwxrwxrwx. 2 root root 72 3月 2 17:48 smbtest
-rw-r--r--. 1 root root 2683 2月 23 15:03 test
The total size is 0.00518036
[root@localhost135 tmp]#

看完了BEING块和END块,我们再来解释一下上述示例中出现的 $0 和 $5的含义
先看一个示例:
[root@localhost135 tmp]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
............................................
too:x:2224:2224::/home/too:/bin/bash
[root@localhost135 tmp]# awk '{print $0}' /etc/passwd # 使用awk print $0 打印出来的结果和 cat 的结果是一样的
root:x:0:0:root:/root:/bin/bash
...........................................
too:x:2224:2224::/home/too:/bin/bash
[root@localhost135 tmp]#
我们可以看到/etc/passwd文件的内容两者是一样的。现在,我们来解释一下awk都做了什么。
当我们调用awk时,我们指定了/etc/passwd作为输入文件,执行awk时,它依次对/etc/passwd中的每一行执行print命令、
awk对输入文件中的每一行都执行这脚本
下面我们来看一张图,便能一目了然的理解其含义
为了更加深刻理解上图含义,我们再来举几个示例
[root@localhost135 tmp]# tail -n2 /etc/passwd |awk -F: '{print $1}'
peter
too
[root@localhost135 tmp]# tail -n2 /etc/passwd |awk -F: '{print $1 $3}'
peter2223
too2224
[root@localhost135 tmp]# tail -n2 /etc/passwd |awk -F: '{print $1" " $3}'
peter 2223
too 2224
[root@localhost135 tmp]# tail -n2 /etc/passwd |awk -F: '{print "username:"$1"\tuid " $3}'
username:peter uid 2223
username:too uid 2224
[root@localhost135 tmp]#
-F: 表示以 冒号 为分隔符
-F 参数:指定分隔符,可指定一个或多个,它是一个内置变量,相当于FS
\t 表示tab键

常用awk内置变量
  1. $0 : 当前记录
  2. $1-$n : 当前记录的第n个字段
  3. FS : 输入字段分隔符,默认是空格
  4. RS : 输入记录分割符,默认是换行符
  5. NF : 当前记录中的字段个数,就是有多少列
  6. NR : 已经读出的记录数,就是行号,从1开始
  7. OPS : 输出字段分隔符,默认是空格
  8. ORS : 输出的记录分割符,默认是换行符
内置变量有很多,但常用的是以上八个内置变量

字段分隔符FS
FS="\t" 一个或多个tab分隔
[root@localhost135 tmp]# cat awktest
I am peter and my phone number is 111111
1:2:3:4:5:6:7:8:9
www baidu com
[root@localhost135 tmp]# awk 'BEGIN{FS="\t+"}{print $1,$2,$3}' awktest
I am peter and my phone number is 111111 # 此行没有tab分隔符
1:2:3:4:5:6:7:8:9 # 此行没有tab分隔符
www baidu com # 此行被分隔成三段,全部打印出
[root@localhost135 tmp]#
FS="[[:sapce:]+]" 一个或多个空白空格,默认的
[root@localhost135 tmp]# cat awktest
I am peter and my phone number is 111111
1:2:3:4:5:6:7:8:9
www baidu com
[root@localhost135 tmp]# awk -F[[:space:]+] '{print $1,$2,$3}' awktest
I am peter
1:2:3:4:5:6:7:8:9
www baidu
[root@localhost135 tmp]#
FS="[" ":]" 以一个或多个空格或者 : 分隔
[root@localhost135 tmp]# cat awktest
I am peter and my phone number is 111111
1:2:3:4:5:6:7:8:9
www baidu com
[root@localhost135 tmp]# awk -F [" ":]+ '{print $1,$2,$3}' awktest
I am peter
1 2 3
www baidu com
[root@localhost135 tmp]#

字段数量 NF
[root@localhost135 tmp]# cat awktest
I am peter and my phone number is 111111
1:2:3:4:5:6:7:8
www baidu com
[root@localhost135 tmp]# awk -F: 'NF==8{print $0}' awktest # 查看有8个字段的行
1:2:3:4:5:6:7:8
[root@localhost135 tmp]#

记录数量NR
[root@localhost135 tmp]# ifconfig ens33 | awk -F [" ":]+ 'NR==2{print $3}' # NR==2也就是去第2行
192.168.148.135
[root@localhost135 tmp]#

RS 记录分隔符变量
将 FS 设置成"\n"告诉 awk 每个字段都占据一行。通过将 RS 设置成"",还会告诉 awk每个地址记录都由空白行分隔。
[root@localhost135 tmp]# cat awktest
hello world
hello

baidu wechat
1234 456 789
[root@localhost135 tmp]# cat awk.bash
#!/bin/awk
BEGIN{
FS="\n"
RS=""
}
{
print $1 "," $2
}
[root@localhost135 tmp]# awk -f awk.bash awktest
hello world,hello
baidu wechat,1234 456 789
[root@localhost135 tmp]#

OFS输出字段分隔符
[root@localhost135 tmp]# cat awkofs
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin:888
[root@localhost135 tmp]# awk 'BEGIN{FS=":"}{print $1","$2","$3}' awkofs
root,x,0
bin,x,1
[root@localhost135 tmp]# awk 'BEGIN{FS=":";OFS="#"}{print $1,$2,$3}' awkofs
root#x#0
bin#x#1
[root@localhost135 tmp]#

ORS 输出记录分隔符
[root@localhost135 tmp]# cat awktest
hello world
hello

baidu wechat
1234 456 789
[root@localhost135 tmp]# cat awk.bash
#!/bin/awk
BEGIN{
FS="\n"
RS=""
ORS="\n\n" # 换行符
}
{
print $1 "," $2
}
[root@localhost135 tmp]# awk -f awk.bash awktest
hello world,hello

baidu wechat,1234 456 789

[root@localhost135 tmp]#


awk 运算符
  1. = :赋值运算符
  2. || : 逻辑或
  3. && : 逻辑与
  4. ! :逻辑非
  5. ~ !~ :匹配正则表达式和不匹配正则表达式
  6. < <= > >= != == : 关系运算符
  7. + - * / & : 加减乘除与求余
  8. ^*** ++ --: 求幂 自加 自减
  9. $ : 字段引用
  10. 空格 : 字符串连接符
  11. ?: :三目运算符
  12. in : 数组中是否存在某键值
以上运算符就不一一演示了,大部分的运算符和平常用的一样,下面演示一下匹配正则表达式的运算符
[root@localhost135 tmp]# awk 'BEGIN{a="123testaa";if(a~/123/){print "ok"}}'
ok
[root@localhost135 tmp]# awk 'BEGIN{a="123testaa";if(a!~/456/){print "ok"}}'
ok
[root@localhost135 tmp]#


awk正则
下图中列出了大部分的awk正则的用法及功能
正则表达式格式:
awk '/REG/{action}' file 其中 /REG/为正则表达式,可将$0中,满足条件的记录送入到action进行处理
举个简单的示例:
[root@localhost135 tmp]# awk '/root/{print $0}' /etc/passwd # 匹配所有包含root的行
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@localhost135 tmp]# awk -F: '$5~/root/{print $0}' /etc/passwd # 以分号作为分隔符,匹配第5个字段是root的行
root:x:0:0:root:/root:/bin/bash
[root@localhost135 tmp]#



布尔表达式
awk '布尔表达式{action}' file 仅当对前面的布尔表达式求值为真时,awk才执行代码块
[root@localhost135 tmp]# awk -F: '$1=="root"{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@localhost135 tmp]# awk -F: '($1=="root")&&($5=="root"){print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@localhost135 tmp]#



评论列表