[TOC]
AWK
介绍
awk
是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。
awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。
AWK
工作原理
第一步:执行BEGIN{action;... }语句块中的语句
第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{ action;... }语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
第三步:当读至输入流末尾时,执行END{action;...}语句块BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中END
语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END
语句块中完成,它也是一个可选语句块pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供
pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块
使用方法
基本格式:awk [options] 'program' file...
program:pattern {action statements;..}
pattern和action:
BEGIN,END
action statements对数据进行处理,放在{}内指明
print,printf
分割符、域和记录
awk执行时,由分隔符分隔的字段(域)标记$1,$2..$n称为域标识。$0为所有域,注意:和shell中变量$符含义不同
文件的每一行称为记录
省略action,则默认执行print $0 的操作
格式和变量
print
格式:
print item1, item2, ...
要点
:
(1) 逗号分隔符
(2) 输出的各item可以字符串,也可以是数值;当前记录的字段、变量或awk的表达式
(3) 如省略item,相当于print $0
awk
内置变量:
FS或-F
:输入字段分隔符,默认为空白字符
FS实例:
[ root@CentOS ~]#awk '{print $1}' score.txt
mage
wang
zhang
li
---------------------------------------------------------------
[ root@CentOS ~]#awk -v FS=':' '{print $1,$3}' score2.txt
mage male
wang male
zhang female
li female
[ root@CentOS ~]#cat score2.txt
mage:100:male
wang:90:male
zhang:80:female
li:100:female
//-v FS=":"以":"分割符,取出文本中的$1(第一列)、$3(第三列)。注意:分割符(":")必须是文本中内容有的,如果文中没有 ":"是没有结果的,如下:
[ root@CentOS ~]#awk -v FS=":" '{print $1,$3}' score.txt
mage 100 male
wang 90 male
zhang 80 female
li 100 female
//他就会打印全部
-F:实例
[ root@CentOS ~]#awk -F: '{print $1,$3}' score2.txt
mage male
wang male
zhang female
li female
//跟FS效果一样,只不过选项格式不一样
OFS
:输出字段分隔符,默认为空白字符
[ root@CentOS ~]#awk -F: -v OFS='-' '{print $1,$3}' score2.txt
mage-male
wang-male
zhang-female
li-female
//先以 ":"分割,OFS='-',指定输出的分隔符
RS
:输入记录分隔符,指定输入时的换行符,原换行符仍有效
[ root@CentOS ~]#awk -v RS=':' '{print $0}' score2.txt
mage
100
male
wang
90
male
zhang
80
female
li
100
female
//-v RS=':' :RS(行处理),在内容中碰到':'就换行,输入时的换行符
ORS
:输出时的换行符
[ root@CentOS ~]#awk -F: -v ORS='--' '{print $0}' score2.txt
mage:100:male--wang:90:male--zhang:80:female--li:100:female--
//先以 ":"分割,输出时以 '--'的分割符
NF
:字段数量
[ root@CentOS ~]#awk -F: '{print NF}' score2.txt
3
3
3
3
1
//查看文件有几个字段,如果写成$NF意义就不一样了。awk 中声明的变量在print 里面无需写'$'
[ root@CentOS ~]#awk -F: '{print $NF}' score2.txt
male
male
female
female
100 female
//打印出score2.txt文件的每行词尾,因为NF是打印每行的字段数量,如果写成$NF那就是NF的结果,如果NF是2,那么输入$NF显示的是2字段的字符串
NR
:行号
[ root@CentOS ~]#awk '{print NR}' score2.txt
1
2
3
4
5
[ root@CentOS ~]#cat score2.txt
mage:100:male
wang:90:male
zhang:80:female
li:100:female
li : 100 female
//显示行号
FNR
:各文件分别计数,行号
[ root@CentOS ~]#awk '{print $0,FNR}' score.txt
mage 100 male 1
wang 90 male 2
zhang 80 female 3
li 100 female 4
//后面的数字显示的是行数编号
FILENAME
:当前文件名
[ root@CentOS ~]#awk '{print FILENAME}' score.txt
score.txt
score.txt
score.txt
score.txt
//因为score.txt里面有5行所有打印了5次,当然我们也可以使用一种模式让他只打印一次
[ root@CentOS ~]#awk 'END{print FILENAME}' score.txt
score.txt
// END后面会讲到
ARGC
:命令行参数的个数
[ root@CentOS ~]#awk '{print ARGC}' score2.txt
2
2
2
2
2
//当前有2个参数,一个是awk,一个是score2.txt,打印5次是因为score2文件中有5行
ARGV
:数组,保存的是命令行所给定的各参数
[ root@CentOS ~]#awk '{print ARGV[0]}' score2.txt
awk
awk
awk
awk
awk
[ root@CentOS ~]#awk '{print ARGV[1]}' score2.txt
score2.txt
score2.txt
score2.txt
score2.txt
score2.txt
//"ARGV[0]":表示数组中的第一个元素,"ARGV[1]":表示数组中的第二个元素
BEGIN/END模式
BEGIN{}
: 仅在开始处理文件中的文本之前执行一次
END{}
:仅在文本处理完成之后执行一次
AWK
自定义变量:
变量(区分字符大小写)
(1) -=value
(2) 在program中直接定义
[ root@CentOS ~]#awk -v test='hello word' 'BEGIN{print test}'
hello word
//定义test变量,并打印出来
[ root@CentOS ~]#awk 'BEGIN{test="hello word"; print test}'
hello word
printf
命令:
格式化输出:printf “FORMAT”, item1, item2, ...
(1) 必须指定FORMAT
(2) 不会自动换行,需要显式给出换行控制符,\n
(3) FORMAT中需要分别为后面每个item指定格式符
格式符:需加"",与item一一对应
%c:显示字符的ASCII码(专门显示字符串)
%d,%i:显示十进制整数
%e,%E 科学技术法数值显示
%f:显示为浮点数
%g,%G:以科学计数法或浮点型式显示数值
%s :显示字符串
%u :无符号整数
%% :显示%自身
修饰符
:
[.#]:第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f
-: 左对齐(默认右对齐)%-15s
+:显示数值的正负符号%+d
[ root@CentOS ~]#awk -F: '{printf "%-10s" "%10d\n",$1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
uucp 10
operator 11
games 12
gopher 13
ftp 14
nobody 99
vcsa 69
saslauth 499
postfix 89
sshd 74
admin1 500
// "%-10s" :向左对齐,s是字符串(一定要跟文件中内容一样,/etc/passwd/中的$1是字符串,$3是数字所以写的是 "%10d"),\n是换行
-----------------------------------------------------------------------------------------
[ root@CentOS ~]#awk -F: '{printf "NAME: %-10s UID:%-10d\n",$1,$3}' /etc/passwd
NAME: root UID:0
NAME: bin UID:1
NAME: daemon UID:2
NAME: adm UID:3
NAME: lp UID:4
NAME: sync UID:5
NAME: shutdown UID:6
NAME: halt UID:7
NAME: mail UID:8
NAME: uucp UID:10
NAME: operator UID:11
NAME: games UID:12
NAME: gopher UID:13
NAME: ftp UID:14
NAME: nobody UID:99
NAME: vcsa UID:69
NAME: saslauth UID:499
NAME: postfix UID:89
NAME: sshd UID:74
NAME: admin1 UID:500
//也可以添加一些字符串然看起来更美观点
操作符
:
算数操作符:
x+y , x-y, x*y , x/y , x^y , x%y
-x:整数转换成负数
+:转换为数值
字符串操作符:没有符号的操作符,字符串链接
赋值操作符:
=, +=, -= , *= , /= , %= ,^=
比较操作符:
>, >=, <, <=, !=, ==
模式匹配符
~ :是否匹配
!~:是否不匹配
逻辑操作符:
&&:并且
||:或者
!:非
条件表达式(三目表达式):
selector?if-true-expression:if-false-expression
//后面案例我们都会用到
awk PATTERN
(1)如果未指定:空模式,匹配每一行
(2) /regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来
[ root@CentOS ~]#awk '/^root/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
//显示以行首(^:行首的意思)为root的行
(3) relational expression:
关系表达式,结果为“真”才会被处理
真:结果为非0值,非空字符串
假:结果为空字符串或0值
[ root@CentOS ~]#awk -F: '$3>200{print $1,$3}' /etc/passwd
saslauth 499
admin1 500
//匹配/etc/passwd中的$3,第三段是否大于200,如果大于就打印出来,否则不打印
[ root@CentOS ~]#awk -F: '$NF=="/bin/bash"{print $NF}' /etc/passwd
/bin/bash
/bin/bash
//$NF每行字段的最后一位字符串,如果匹配到的是 "/bin/bash"就打印出来
三目表达式
:
[ root@CentOS ~]#awk -F: '{$3<500?user="is system":user="is common";{printf "%-10s %10s\n" ,$1,user}}' /etc/passwd
root is system
bin is system
daemon is system
adm is system
lp is system
sync is system
shutdown is system
halt is system
mail is system
uucp is system
operator is system
games is system
gopher is system
ftp is system
nobody is system
vcsa is system
saslauth is system
postfix is system
sshd is system
admin1 is cmmon
//以":"分割,/etc/passwd中的第三行(uuid)小于500的就输出"is system ",否则就输入"is common",
//三目表达式格式:selector?if-true-expression:if-false-expression
控制语句
if(condition) {statments}
if(condition) {statments} else {statments}
while(conditon) {statments}
do {statements} while(condition)
for(expr1;expr2;expr3) {statements}
break 、continue 、delete arry[index] 、delete arry 、exit {statements}
if else 案例
:
[ root@CentOS ~]#awk -F: '{if($3>=200) {printf "Common user: %-10s %8s\n", $1,$3}else{printf "is system %-10s %10s\n",$1,$3}}' /etc/passwd
is system root 0
is system bin 1
is system daemon 2
is system adm 3
is system lp 4
is system sync 5
is system shutdown 6
is system halt 7
is system mail 8
is system uucp 10
is system operator 11
is system games 12
is system gopher 13
is system ftp 14
is system nobody 99
is system vcsa 69
Common user: saslauth 499
is system postfix 89
is system sshd 74
Common user: admin1 500
// if判断$3(/etc/passwd中的id)>=200就打印"Common user: $1,$3",否则就打印小于200(/etc/passwd中id)小于200的用户和id("is system $1,$3")
while和for 循环
[ root@localhost ~]#awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) { if(length($i)>=3) {print $i,length($i)};i++}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-514.el7.x86_64 30
root=UUID=25927f3c-9f12-45f7-8995-652b4468aebe 46
crashkernel=auto 16
net.ifnames=0 13
linux16 7
/vmlinuz-0-rescue-e2b1c08415194abdb3672f06ea53672b 50
root=UUID=25927f3c-9f12-45f7-8995-652b4468aebe 46
crashkernel=auto 16
net.ifnames=0 13
//对/etc/grub2.cfg进行操作,以行首为空白开头最少一次或者多次并且后面跟linux16的行(^[[:space:]]*linux16),"i=1;while(i<=NF)",NF(统计每行的字段),i小于等于每行的字段总数,
if(length($i)>=3:意思是每行的每段的字符串字数大于等于3的打印出来,最后的{print $i,length($i)};i++}}
意思是:打印$i就是每行的段,length($i):将次匹配到的字符串放入这里面,最后的i++每次i的值都会+1,从1一直加到小于等于每行的字段数(只匹配设定前面设定条件的行)
//length 字符串长度
-----------------------------------------------------------------------------------------------
[ root@localhost ~]#awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++){ if(length($i)>=2) {print $i,length($i)}}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-514.el7.x86_64 30
root=UUID=25927f3c-9f12-45f7-8995-652b4468aebe 46
ro 2
crashkernel=auto 16
net.ifnames=0 13
linux16 7
/vmlinuz-0-rescue-e2b1c08415194abdb3672f06ea53672b 50
root=UUID=25927f3c-9f12-45f7-8995-652b4468aebe 46
ro 2
crashkernel=auto 16
net.ifnames=0 13
//跟上面效果一样,只不过换成for循环
数组
:
[ root@CentOS ~]#awk 'BEGIN{user["w"]="wang";user["m"]="ma";print user["m"]}'
ma
// 声明一个数组,[]里面声明了2个元素,元素和值必须加双引号,打印时也许要输入双引号
//注意:var会遍历array的每个索引
--------------------------------------------------------------------------------------------
[ root@CentOS ~]#awk 'BEGIN{user["w"]="wang";user["m"]="ma";for(i in user) {print user[i]}}'
wang
ma
//通过for循环打印出数组中的元素