AWK学习总结

AWK是一种优良的文本处理工具。其名称得自于它的创始人阿尔佛雷德·艾侯、彼得·温伯格和布莱恩·柯林汉姓氏的首个字母。AWK提供了极其强大的功能:可以进行正则表达式的匹配,样式装入、流控制、数学运算符、进程控制语句甚至于内置的变量和函数。它具备了一个完整的语言所应具有的几乎所有精美特性。


AWK的基本用法

awk [options] ‘program’ var=value file…
awk [options] -f programfile var=value file…
awk [options] 'BEGIN{ action;… } pattern{ action;… } END{action;… }' file ...

awk 程序通常由: BEGIN语句块、能够使用模式匹配的通用语句块、 END语句块,共3部分组成 
program通常是被单引号或双引号中 
基本格式:

awk [options] 'program' file…
program:pattern{action statements;..}

pattern和action: 
pattern部分决定动作语句何时触发及触发事件( BEGIN,END) 
action statements对数据进行处理,放在{}内指明( print, printf) 
分割符、域和记录: 
awk执行时, 由分隔符分隔的字段(域)标记2..0为所有域,注意:和shell中变量0 的操作;

工作原理:

第一步:执行BEGIN{action;… }语句块中的语句; 
第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{action;… }语句块,它逐 行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕; 
第三步:当读至输入流末尾时,执行END{action;…}语句块; 
BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如 变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中; 
END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块; 
pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行, awk读取的每一行都会执行该语句块;

常用选项

-F:指明输入数据的字段分割符,默认为空白符;该选项的用法类似于命令cut的选项-f;
-v:自定义变量,用法为:-v var=VALUE;

变量

内建变量

FS:输入数据字段分隔符,默认为空白符;

[root:~]#    awk -v FS=":" '{print $1,$3}' /etc/passwd          
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
##指明“:”作为字段分隔符,然后输出第一字段和第三字段,输出的字段默认分隔符为空白字符

OFS:指定输出字段的分隔符,默认为空白字符;

[root:~]#    awk -v FS=":" -v OFS="@@@@@@@@@@" '{print $1,$3}' /etc/passwd
root@@@@@@@@@@0
bin@@@@@@@@@@1
daemon@@@@@@@@@@2
adm@@@@@@@@@@3
lp@@@@@@@@@@4
sync@@@@@@@@@@5
shutdown@@@@@@@@@@6
##指定的输出分隔符为“@@@@@@@@@”,所以输出的两个字段之间就不在是空白;

注意:在使用内置变量FS和OFS时,都需要用-v选项声明。 
RS:指定输入时的换行符; 
ORS:指定输出是的换行符:

[root:~]#    awk -v RS=":" -v ORS="#" '{print $0}' /etc/passwd
root#x#0#0#root#/root#/bin/bash
bin#x#1#1#bin#/bin#/sbin/nologin
daemon#x#2#2#daemon#/sbin#/sbin/nologin
adm#x#3#4#adm#/var/adm#/sbin/nologin
lp#x#4#7#lp#/var/spool/lpd#/sbin/nologin
##上面语句指定“:”为输入时的行分隔符,而将输出的行分隔符设定为“#”,所以输出的字段由#分割的是行;

NF:每一行的字段数量,即每一行被默认的或者指定的分隔符分割成了多少个字段:

[root:~]#    awk -v FS=":" '{print NF,$NF}' /etc/passwd
7 /bin/bash
7 /sbin/nologin
7 /sbin/nologin
7 /sbin/nologin
7 /sbin/nologin
7 /bin/sync

注意上例中,NF是7,代表分隔符“:”将文件中的每行分成了7可字段,而$NF则是分隔符将每行分成7个字段中的最后一个字段。

NR和FNR:输出时显示行号,它们之间的不同之处在于:在同时输出多个文件时,NR会将所有文件的行号连续显示,而FNR会单独显示每个文件的行号;

[root:~]#    awk '{print NR,$0}' /etc/issue /etc/fstab
1 \S
2 Kernel \r on an \m
3
4 Mage Education Learning Services
5 http://www.magedu.com
6
7
8 #
9 # /etc/fstab
10 # Created by anaconda on Wed Nov  2 21:36:59 2016
11 #
12 # Accessible filesystems, by reference, are maintained under '/dev/disk'
13 # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
14 #
15 UUID=aff70d76-16d5-4559-97cb-9a83f2f638d3 /                       xfs     defaults        0 0
16 UUID=8e197e5f-2a94-48c5-a5b6-044d83f8448d /boot                   xfs     defaults        0 0
17 UUID=c378a443-213b-408a-975b-d87b8bc0b1a5 /usr                    xfs     defaults        0 0
18 UUID=1e0def53-6464-422b-b713-9bf0d17faad0 swap                    swap    defaults        0 0
##NR将两个文件的行号连续输出了


[root:~]#    awk '{print FNR,$0}' /etc/issue /etc/fstab
1 \S
2 Kernel \r on an \m
3
4 Mage Education Learning Services
5 http://www.magedu.com
6
1
2 #
3 # /etc/fstab
4 # Created by anaconda on Wed Nov  2 21:36:59 2016
5 #
6 # Accessible filesystems, by reference, are maintained under '/dev/disk'
7 # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
8 #
9 UUID=aff70d76-16d5-4559-97cb-9a83f2f638d3 /                       xfs     defaults        0 0
10 UUID=8e197e5f-2a94-48c5-a5b6-044d83f8448d /boot                   xfs     defaults        0 0
11 UUID=c378a443-213b-408a-975b-d87b8bc0b1a5 /usr                    xfs     defaults        0 0
12 UUID=1e0def53-6464-422b-b713-9bf0d17faad0 swap                    swap    defaults        0 0

##FNR将每个文件的行号单独显示

ARGC:命令行参数的个数。这里值得注意的是:awk是命令行的参数; 
ARGV:保存的是命令行给定的参数的数组,awk是命令行的第一个参数;

[root:~]#    awk 'END{print ARGC}' /etc/issue /etc/fstab
3
[root:~]#    awk 'END{print ARGV[0]}' /etc/issue /etc/fstab
awk
[root:~]#    awk 'END{print ARGV[1]}' /etc/issue /etc/fstab
/etc/issue
[root:~]#    awk 'END{print ARGV[2]}' /etc/issue /etc/fstab
/etc/fstab
[root:~]#    awk 'END{print ARGV[3]}' /etc/issue /etc/fstab

[root:~]#  
##ARGC将awk作为一个参数,所以显示为3,而ARGV[0]显示awk,则ARGV将awk作为了数组的第一个值。  

自定义变量

自定义变量的格式为:-v VAR=VALVE 
可以将变量定义在program外,也可以定义在program中,两者不同之处是一个要用-v声明,一个不用;

[root:~]#    awk -v test="hello world" 'BEGIN{print test}' /etc/issue
hello world
[root:~]#    awk  'BEGIN{test="hello world";print test}' /etc/issue
hello world

输出打印(print,printf)

print格式:print item1, item2, … 
逗号分隔符,输出的各item可以字符串,也可以是数值;当前记录的字段、变量或awk的表达式,如省略item,相当于print $0;

[root:~]#    awk '/^UUID/{print $1"\t"$3}' /etc/fstab
UUID=aff70d76-16d5-4559-97cb-9a83f2f638d3   xfs
UUID=8e197e5f-2a94-48c5-a5b6-044d83f8448d   xfs
UUID=c378a443-213b-408a-975b-d87b8bc0b1a5   xfs
UUID=1e0def53-6464-422b-b713-9bf0d17faad0   swap

[root:~]#    awk '/^UUID/{print}' /etc/fstab
UUID=aff70d76-16d5-4559-97cb-9a83f2f638d3 /                       xfs     defaults        0 0
UUID=8e197e5f-2a94-48c5-a5b6-044d83f8448d /boot                   xfs     defaults        0 0
UUID=c378a443-213b-408a-975b-d87b8bc0b1a5 /usr                    xfs     defaults        0 0
UUID=1e0def53-6464-422b-b713-9bf0d17faad0 swap                    swap    defaults        0 0

printf格式:printf FORMAT, item1, item2, … 
注意FORMAT必须给出,在显示多行时,不会自动换行,要给定换行符“\n”;并且需要分别对后面要输出的每个item指定输出格式; 
格式符

%c: 显示字符的ASCII码 
%d, %i: 显示十进制整数 
%e, %E:显示科学计数法数值 
%f:显示为浮点数 
%g, %G:以科学计数法或浮点形式显示数值 
%s:显示字符串 
%u:无符号整数 
%%: 显示%自身

修饰符

#[.#]:第一个数字控制显示的宽度;第二个#表示小数点后精度, %3.1f 
-: 左对齐(默认右对齐) %-15s 
+:显示数值的正负符号 %+d

[root:~]#    awk -v FS=":" '{printf "username:%-20s UID:%5.2f\n",$1,$3}' /etc/passwd
username:root                 UID: 0.00
username:bin                  UID: 1.00
.
.
username:nfsnobody            UID:65534.00
username:postfix              UID:89.00
username:sshd                 UID:74.00
username:ntp                  UID:38.00
username:tcpdump              UID:72.00
## "username:%-20s UID:%5.2f\n"中的%-20s是左对齐20个字符显示字符串,%5.2f是以浮点数形式显示后面的UID数值。

操作符

awk类似shell语言,也有类似shell的操作符,如算术操作符、赋值操作符、比较操作符、模式匹配操作符等等,下面列出了常用的操作符:

操作符类型 操作符
算术操作符 +,-,*,/,^,%,-x,+x
比较操作符 <,<=,>,>=,==,!=
赋值操作符 =,+=,-=,*=,/=,%=,^=,++,–
模式匹配操作符 ~,!~
逻辑操作符 &&,!,II

函数调用:function_name(argu1,argu2,…) 
条件表达式(三目表达式):selector?if-true-expression:if-false-expression

[root:~]#    awk -F: '{$3>=1000?tpye="common user":tpye="sys user";printf "%-30s %d\n",tpye":"$1,$3}' /etc/passwd
sys user:root                  0
sys user:bin                   1
sys user:daemon                2
sys user:adm                   3
sys user:lp                    4
sys user:sync                  5
sys user:shutdown              6
sys user:halt                  7
sys user:mail                  8
sys user:operator              11
sys user:games                 12
sys user:ftp                   14
sys user:nobody                99
sys user:avahi-autoipd         170
sys user:systemd-bus-proxy     999
sys user:systemd-network       998
sys user:dbus                  81
sys user:polkitd               997
sys user:abrt                  173
sys user:colord                996
sys user:libstoragemgmt        995
sys user:setroubleshoot        994
sys user:rpc                   32
sys user:rtkit                 172
sys user:chrony                993
sys user:tss                   59
sys user:geoclue               992
sys user:usbmuxd               113
sys user:mysql                 27
sys user:pulse                 171
sys user:gdm                   42
sys user:rpcuser               29
common user:nfsnobody          65534
sys user:postfix               89
sys user:sshd                  74
sys user:ntp                   38
sys user:tcpdump               72
common user:hacker             1001

PATTERN

awk中可以根据pattern过滤行:

空模式(empty),匹配每一行

[root:~]#    awk '{print}' /etc/issue
\S
Kernel \r on an \m

/RGEXP/:仅处理匹配到的行

[root:~]#    awk '/^#/{print}' /etc/fstab
#
# /etc/fstab
# Created by anaconda on Wed Nov  2 21:36:59 2016
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#

关系表达式:结果为真则处理,结果不为真则不处理

[root:~]#    df | awk '$1~"/dev/"{print}'
/dev/sda2       41922560  1556776  40365784   4% /
/dev/sda3       20961280 12422564   8538716  60% /usr
/dev/sda1         508588   193720    314868  39% /boot

地址定界:仅处匹配到的范围行

[root:~]#    awk '/^root/,/^ftp/{print}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
###从匹配到以root开头的行开始,到匹配到以ftp开头的行结束

BEGIN/END模式:

BEGIN{}:仅在开始处理文件之前执行一次 
END{}:仅在文本处理完成之后执行一次

[root:~]#    df |awk 'BEGIN{printf "%-10s%-4s\n%s\n","DISK","USED","----------------"}/^\/dev/{print $1,$5}END{print "================\n","end\n"}'
DISK      USED
----------------
/dev/sda2 4%
/dev/sda3 60%
/dev/sda1 39%
================
end

action

常用的action有:

表达式:如算术表达式,比较表达式; 
控制语句:和shell相似的if、while、for等控制语句; 
组合语句:多条控制语句组合在一起; 
输入、输出语句:如print、printf语句; 
awk的使用中,经常能看见很多用法和shell语言相似的地方;一样有if语句,有while语句,for循环语句等。它们的使用是awk工具更好用,功能更强大。

if语句语法:if(condition){statement}[else{statement}]。举例:

## if
[root:~]#    awk -F: '{if($3>=1000)printf "%-15s%d\n",$1,$3}' /etc/passwd
nfsnobody      65534
hacker         1001
## if-else
[root:~]#    awk -F: '{if($3>=1000)printf "%-15s%d\n",$1,$3;else print "Sys User:",$1}' /etc/passwd
Sys User: root
Sys User: bin
.
.
Sys User: gdm
Sys User: rpcuser
nfsnobody      65534
Sys User: postfix
Sys User: sshd
Sys User: ntp
Sys User: tcpdump
hacker         1001

while语句语法:while(condition){statement},当条件为非0或者非空字符串时为真,执行后面语句; 
使用场景:对一行内的多个字段逐一处理时使用;对数组内的元素逐一处理时使用。

[root:~]#    awk -F: '{i=0;while(i<=NF){if(length($i)>100)printf "user:%s have passwd: %s\n",$1,$2;i++}}' /etc/shadow
user:root have passwd: $6$tR88V6XX$Dmy2cI.zo5UT.ndn0uoufO0cCfOssRIhs/OJcPA58SQcykUGkC83RnI4sbrjXaWQ71CTVx4xslsskL2jf6r2A0
user:hacker have passwd: $6$zK50RPcQ$JADFdJDBJQ8f1RzT5LzxAIbE/pLtcdyYR9Cw2ZneebLEUgq3ltyPk0fJ0FM9hhZl3P2K8ak0b3vZEeOOO6az11

do-while语法:do{statement}while(condition),该语句和while语句的不同之处就是不管条件是否为真,都执行一次语句在判断条件是否为真。

for语句的语法:for(expr1;expr2;expr3){statement},此处的for用户和shell语句中的for特殊用法一样。

[root:~]#    awk 'BEGIN{for(i=1;i<=100;i++)sum+=i;print sum}'
5050

注意:在awk中,for循环语句也有特殊用法,它可以遍历数组中的元素,这个特殊用法我们在下面数组中演示。

switch语句语法:switch(condition){case VALUE1 or REGEXP1:statement1;case VALUE2 or REGEXP2:statement;…;default:statement}

注意:在awk的循环语句中,同样支持continuebreak语法,用法和shell语句中的一样。不仅如此,awk还支持next语法,用法和continuebreak一样,不过它的作用是跳出当前行循环,进行下一样的处理,例如:

[root:~]#    awk -F: '{if($3%2==1)next;print NR,$0}' /etc/passwd
1 root:x:0:0:root:/root:/bin/bash
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
11 games:x:12:100:games:/usr/games:/sbin/nologin
12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
14 avahi-autoipd:x:170:170:Avahi IPv4LL Stack:/var/lib/avahi-autoipd:/sbin/nologin
16 systemd-network:x:998:996:systemd Network Management:/:/sbin/nologin
20 colord:x:996:994:User for colord:/var/lib/colord:/sbin/nologin
22 setroubleshoot:x:994:991:setroubleshoot,,62985600:/var/lib/setroubleshoot:/sbin/nologin
23 rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
24 rtkit:x:172:172:RealtimeKit:/proc:/sbin/nologin
27 geoclue:x:992:989:User for geoclue:/var/lib/geoclue:/sbin/nologin
31 gdm:x:42:42:gdm,,62985600:/var/lib/gdm:/sbin/nologin
33 nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
35 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
36 ntp:x:38:38:ntp,,62985600:/etc/ntp:/sbin/nologin
37 tcpdump:x:72:72:tcpdump,,62985600:/:/sbin/nologin

数组

关联数组: array[index-expression]

index-expression:

(1) 可使用任意字符串;字符串要使用双引号括起来; 
(2) 如果某数组元素事先不存在,在引用时, awk会自动创建此元素,并将其值初始化为“空串”;

若要判断数组中是否存在某元素,要使用“index in array”格式进行遍历(index是关键字,就不要在awk脚本中使用); 
如统计某时刻,网络监听情况,就可以是使用数组遍历的方式来统计:

root:~]#    netstat -nta | awk '/^tcp\>/{print $4}' | awk -F: '{IP[$1]++}END{for(i in IP){print i,IP[i]}}'
127.0.0.1 2
0.0.0.0 1
172.16.252.5 1
## IP[$1]++将ip地址作为数组ip的索引号,来初始化数组元素,当相同的ip出现,则将数组元素的值加1,这样,就把ip出现的次数统计成数组元素,输出数组索引号和数组元素就把ip以及出现的次数统计出来了。

例如:统计/etc/fstab文件中每个文件系统类型出现的次数

[root:~]#    awk '!/^#/&&!/^$/{count[$3]++}END{for(i in count)print i,count[i]}' /etc/fstab
swap 1
xfs 3

内置函数

函数rand():

返回一个0~1之间的一个随机数,需要配合函数srand()一起使用:

[root:~]#    awk 'BEGIN{srand();print rand()}'
0.350256
[root:~]#    awk 'BEGIN{srand();print rand()}'
0.608501
[root:~]#    awk 'BEGIN{srand();print rand()}'
0.811887
[root:~]#    awk 'BEGIN{srand();print rand()}'
0.676783
[root:~]#    awk 'BEGIN{srand();print rand()}'
0.676783
[root:~]#    awk 'BEGIN{srand();print rand()}'
0.0149217
[root:~]#    awk 'BEGIN{srand();print rand()}'
0.804012
##如果没有函数srand(),返回的值不变:
[root:~]#    awk 'BEGIN{print rand()}'
0.237788
[root:~]#    awk 'BEGIN{print rand()}'
0.237788
[root:~]#    awk 'BEGIN{print rand()}'
0.237788

函数length()

返回一个字符串的长度:

[root:~]#    awk -F: '{if($2!="*"&&$2!="!!")print $1,length($2)}' /etc/shadow
root 98
hacker 98

函数int()

取值整数:

[root:~]#    awk 'BEGIN{srand();print int(rand()*100)}'
69
[root:~]#    awk 'BEGIN{srand();print int(rand()*100)}'
43
[root:~]#    awk 'BEGIN{srand();print int(rand()*100)}'
2
[root:~]#    awk 'BEGIN{srand();print int(rand()*100)}'
93
[root:~]#    awk 'BEGIN{srand();print int(rand()*100)}'
93
[root:~]#    awk 'BEGIN{srand();print int(rand()*100)}'
93

函数sub(r,s,[t])、gsub(s,r,[t])

sub函数以r表示的模式来查找t所表示的字符串中的匹配的内容,并将其第一次出现替换为s所表示的内容;gsub函数以r表示的模式来查找t所表示的字符串中的匹配的内容,并将所有能匹配到的替换为s所表示的内容。

[root:~]#    awk 'sub(/root/,"rooter",$0)' /etc/passwd
rooter:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/rooter:/sbin/nologin
[root:~]#    awk 'gsub(/root/,"rooter",$0)' /etc/passwd
rooter:x:0:0:rooter:/rooter:/bin/bash
operator:x:11:0:operator:/rooter:/sbin/nologin

函数split(s,array,[r])

以r为分隔符,切割字符s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2,…

[root:~]#   cat /var/log/httpd/access_log | awk '{split($0,IP," ");count[IP[1]]++}END{for(i in count)print i,count[i]}'
127.0.0.1 2
172.16.250.92 4

自定义函数

格式: 
function name ( parameter, parameter, … ) { 
statements 
return expression 

parameter是自定义函数的形式参数,是指定函数需要一个参数的表示,在真正使用函数的过程中,可以根据实际要求定义。 
示例: 
cat fun.awk 
function max(v1,v2) { 
v1>v2?var=v1:var=v2 
return var 

BEGIN{a=3;b=2;print max(a,b)} 
awk –f fun.awk

在awk语句中点用shell命令

使用system引用shell命令;空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了awk的变量外其他一律用”“引用起来。 
在awk中语句中,可以使用shell命令,如下:

[root:~]#   awk 'BEGIN{system("uname -r")}'
2.6.32-642.el6.x86_64
#########################################
[root:~]#   awk 'BEGIN{system("[ 3 > 2 ] && echo right")}'
right

awk脚本

将awk程序写成脚本,直接调用或执行 
示例:

cat f1.awk
if($3>=1000)print $1,$3}
#awk -F: -f f1.awk /etc/passwd
#cat f2.awk
#!/bin/awk –f
#this is a awk script
{if($3>=1000)print $1,$3}
#chmod +x f2.awk
#f2.awk –F: /etc/passwd

向awk脚本传递参数

格式:awkfile var=value var2=value2… Inputfile 
示例:

#cat test.awk
#!/bin/awk –f
{if($3 >=min && $3<=max)print $1,$3}
#chmod +x test.awk
#test.awk -F: min=100 max=200 /etc/passwd

原创文章,作者:王更生,如若转载,请注明出处:http://www.178linux.com/62146

联系我们

400-080-6560

在线咨询

工作时间:周一至周五,9:30-18:30,节假日同时也值班

QR code