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的循环语句中,同样支持continue、break语法,用法和shell语句中的一样。不仅如此,awk还支持next语法,用法和continue、break一样,不过它的作用是跳出当前行循环,进行下一样的处理,例如:
[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