AWK的进阶使用

AWK的语法格式:awk [option]...'/PATTERN/{action}' FILE;


1、AWK的输出,该语句通常要写在AWK语句中的action字段:

print item1,item2,...

要点:

(1)各项目之间要使用逗号分隔,而输出时则使用输出分隔符分隔;

(2)输出的各item可以是字符串或数值、当前记录的字段、变量或AWK的表达式;数值会被隐式的转换为字符串后输出;

(3)print后面item如果省略,相当于print $0;而如果要输出空白,要使用print "";

2、AWK的变量

AWK有内置变量,同时还支持自定义变量

    2.1 内置变量

FS:Field Seperator,输入时的字段分隔符。

[root@client1 ~]# awk 'BEGIN{FS=":"}{print$1,$7}' /etc/passwd
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
sync /bin/sync
shutdown /sbin/shutdown
halt /sbin/halt
mail /sbin/nologin
uucp /sbin/nologin
operator /sbin/nologin
games /sbin/nologin
gopher /sbin/nologin

RS:Record Seperator,输入时的行分隔符,以指定分隔符分隔的字段作为一个整行输出,如果指定的分隔符在行中没有,那么默认情况下会显示整行内容,示例:

[root@localhost ~]# awk 'BEGIN{RS=":"}{print}' /etc/passwd
root
x
0
0
root
/root
/bin/bash
bin
x
1
1
bin
/bin
/sbin/nologin

显示的结果为以冒号为分隔每个字段为一行进行显示。

OFS:输出分隔符,默认分隔符为空格,使用示例:

[root@localhost ~]# awk 'BEGIN{FS=":";OFS=":"}{print $1,$7}' /etc/passwd
root:/bin/bash
bin:/sbin/nologin
daemon:/sbin/nologin
adm:/sbin/nologin
lp:/sbin/nologin
sync:/bin/sync

ORS:输出时的行分隔符,示例:

[root@localhost ~]# awk 'BEGIN{FS=":";ORS=":"}{print $1,$7}' /etc/passwd
root /bin/bash:bin /sbin/nologin:daemon /sbin/nologin:adm /sbin/nologin:lp /sbin/nologin:sync /bin/sync:shutdown /sbin/shutdown:halt /sbin/halt:mail /sbin/nologin:uucp /sbin/nologin:operator /sbin/nologin:games /sbin/nologin:gopher /sbin/nologin:ftp /sbin/nologin:nobody /sbin/nologin:dbus /sbin/nologin:usbmuxd /sbin/nologin:vcsa /sbin/nologin:rtkit /sbin/nologin:rpc /sbin/nologin:avahi-autoipd /sbin/nologin:abrt /sbin/nologin:haldaemon /sbin/nologin:gdm /sbin/nologin:ntp /sbin/nologin:apache /sbin/nologin:saslauth /sbin/nologin:postfix /sbin/nologin:rpcuser /sbin/nologin:nfsnobody /sbin/nologin:pulse /

FS,RS,OFS,RFS四个变量用于PATTERN中,要注意这几个变量的使用位置。

NF:每行的字段总数:


NR:文件使用输入分隔符统计出来的行。如果有多个文件,行号是一并计算的。

[root@localhost ~]# awk 'BEGIN{FS=":";OFS=":"}{print NR,$1,$7}' /etc/passwd
1:root:/bin/bash
2:bin:/sbin/nologin
3:daemon:/sbin/nologin
4:adm:/sbin/nologin
5:lp:/sbin/nologin
6:sync:/bin/sync

FNR:每个文件使用输入分隔符分隔的行对应的行号,如果有多个文件,每个文件的行号在输出时是单独计算的。

[root@localhost ~]# awk 'BEGIN{FS=":";OFS=":"}{print FNR,$1,$7}' /etc/passwd /etc/fstab
1:root:/bin/bash
2:bin:/sbin/nologin
3:daemon:/sbin/nologin
4:adm:/sbin/nologin
5:lp:/sbin/nologin
6:sync:/bin/sync
7:shutdown:/sbin/shutdown
8:halt:/sbin/halt
9:mail:/sbin/nologin
1::
2:#:
3:# /etc/fstab:
4:# Created by anaconda on Mon Aug 25 12:
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:/dev/mapper/vg0-root    /                       ext4    defaults        1 1:
10:UUID=2de75192-ea16-4c27-a665-2a3c0a5a9592 /boot                   ext4    defaults        1 2:
11:/dev/mapper/vg0-usr     /usr                    ext4    defaults        1 2:
12:/dev/mapper/vg0-var     /var                    ext4    defaults        1 2:
13:/dev/mapper/vg0-swap    swap                    swap    defaults        0 0:
14:tmpfs                   /dev/shm                tmpfs   defaults        0 0:
15:devpts                  /dev/pts                devpts  gid=5,mode=620  0 0:
16:sysfs                   /sys                    sysfs   defaults        0 0:
17:proc                    /proc                   proc    defaults        0 0:

ARGV:数组,保存命令本身这个字符,awk '{print $0}' 1.txt 2.txt,意味着ARGV[0]保存awk。

ARGC: 保存awk命令中参数的个数,示例:

[root@localhost ~]# awk 'BEGIN{print ARGV[0],ARGV[1],ARGC}' /etc/passwd /etc/group
awk /etc/passwd 3

FILENAME: awk正在处理的当前文件的名称;

注:NF,NR,FNR,ARGV,ARGC几个变量的应用场合是在AWK语句的action中。


    2.2、自定义变量

AWK中定义变量与Linux系统中自动以变量的方式相同,变量名可使用字母、数字和下划线,但不能以数字开头。变量的定义语法格式:-v var_name=VALUE,AWK中自定义变量的使用位置:

(1) 可以在AWK命令行的script段中定义变量,示例:

[root@localhost ~]# awk 'BEGIN{a="hello awk";print a}'
hello awk

(2) 可以命令行中通过-v选项自定义变量;

[root@localhost ~]# awk -v a="hello gawk" 'BEGIN{print a}'
hello gawk

注意:BEGIN这个模式是在行处理之前所做的一些特定的处理操作,如果在BEGIN模式中有相应的命令语句,那么可以不加文件参数,就可以达到输出的效果,此时命令语句要写在BEGIN的模式中,而不能单独定义在AWK的action字段中,AWK语句书写完提交执行时在进行语法分析时以这些模式定义字符以及特殊字符如{}()等等来判断其中活其后面的内容属于语法定义的哪一部分,PATTERN,ACTION还是模式定义等,这些模式的定义具体位置在程序运行时是有固定位置和使用方法的,不能随意混用。

AWK的语法中,{}括起来的部分可以理解为是action,在BEGIN和END模式中定义的FS,OFS等赋值操作,虽然其中没有命令执行语句,但是也需要{},在这两种模式中可以有命令语句也可以没有,但没有命令语句的情况下,AWK语句中一定要有对整个文件每一行都进行处理的单独action语句的定义,否则AWK语句在执行时会如同cat命令一样,程序处于等待用户输入的状态,用户每输入一行,AWK会处理一行并显示,之后恢复到等待输入的状态。

3、AWK的printf语句

printf语句在AWK语句中是将要输出的内容以指定的格式先进行格式化操作之后再进行输出,所以printf语句在处理的内容之前需要先指定格式,格式字符串与要处理的内容之前用逗号分隔,其表现形式类似于要处理的内容。printf语法格式为:printf format, item1, item2,...

printf语句书写的要点:  

    (1) 要指定format;
    (2) 不会自动换行;如需换行则需要给出\n
    (3) format用于为后面的每个item指定其输出格式;

format格式的指示符都以%开头,后跟一个字符:

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

format的格式字符串还可以使用修饰符,使用格式修饰符时,修饰字符在格式符之前:

    #:显示宽度
    -:左对齐
    +:显示数值的符号
    .#: 取值精度

printf语句使用示例:

指定输出字段所占的显示宽度

[root@localhost ~]# awk -F: '{printf "%20s,%30s\n",$1,$7}' /etc/passwd
                root,                     /bin/bash
                 bin,                 /sbin/nologin
              daemon,                 /sbin/nologin
                 adm,                 /sbin/nologin
                  lp,                 /sbin/nologin
                sync,                     /bin/sync
            shutdown,                /sbin/shutdown

指定输出字段的对齐方式,默认是右对齐:

[root@localhost ~]# awk -F: '{printf "%-20s,%30s\n",$1,$7}' /etc/passwd
root                ,                     /bin/bash
bin                 ,                 /sbin/nologin
daemon              ,                 /sbin/nologin
adm                 ,                 /sbin/nologin
lp                  ,                 /sbin/nologin
sync                ,                     /bin/sync
shutdown            ,                /sbin/shutdown
halt                ,                    /sbin/halt

指定输出数值的数值类型,浮点数示例:

[root@localhost ~]# awk 'BEGIN{printf "%f\n",3.1415}'
3.141500
[root@localhost ~]# awk 'BEGIN{printf "%g\n",3.1415926}'
3.14159

以科学计数法显示数值,示例:

[root@localhost ~]# awk 'BEGIN{printf "%e\n",3.1415926}'
3.141593e+00

指定输出数值的精度:  

[root@localhost ~]# awk 'BEGIN{printf "%-15.2e\n",314159.26}'
3.14e+0

4、AWK的输出重定向:默认输出时输出至屏幕,输出重定向的语法格式为:

print items > output-file
print items >> output-file
print items | command

5、AWK的运算操作符

    5.1、算术操作符:

x+y
x-y
x*y
x/y
x**y, x^y
x%y
-x:负值
+x:转换为数值

    5.2、字符串操作符:AWK中的字符串操作符是连接操作符,该操作符没有具体的字符来表达,字符串连接操作只需要在输出时连续输入对应的字符串即达到连接的效果。

    5.3、赋值操作符:

=
+=
-=
*=
/=
%=
^=
**=
++
--
/=/

    5.4、比较操作符:

<
<=
>
>=
==
!=
~:模式匹配,左边的字符串能够被右边的模式所匹配为真,否则为假;
!~:

    5.5、逻辑操作符:

&&: 与
||:或

    5.6、条件表达式

语法格式:selector?if-true-expression:if-false-expression

示例,判断用户的ID号,如果大于等于500,显示该用户为普通用户,如果小于500就显示该用户为管理员或者系统用户:

[root@localhost ~]# awk -F: '{$3>=500?utype="common user":utype="admin or system user";print $1,"is",utype}' /etc/passwd
root is admin or system user
bin is admin or system user
daemon is admin or system user
adm is admin or system user
lp is admin or system user
sync is admin or system user
nfsnobody is common user

    5.7、函数调用

语法格式:function_name(argu1,argu2)

6、AWK语句中的模式

可用类型有如下几类:

(1) Regexp: 格式为/PATTERN/
    仅处理被/PATTERN/匹配到的行;
(2) Expression: 表达式,其结果为非0或非空字符串时满足条件;
    仅处理满足条件的行;示例:
   [root@localhost ~]# awk -F: '$3>=500{printf "%12s %-5d\n",$1,$3}' /etc/passwd
   nfsnobody 65534
(3) Ranges: 行范围,此前地址定界,startline, endline,startline和endline使用的是模式表达式,指定行范围后AWK仅处理范围内的行
(4) BEGIN/END: 特殊模式,仅在awk命令的program运行之前(BEGIN)或运行之后(END)执行一次;
(5) Empty:空模式,匹配任意行;

7、常用的action

(1) Expressions
(2) Control statements
(3) Compound statements
(4) input statements
(5) output statements

8、AWK中的控制语句,控制语句要写在AWK语句中的action字段。

    8.1 if-else,使用格式:if (condition) {then body} else {else body},示例:显示/etc/inittab文件中字段数大于8的行:

[root@localhost ~]# awk '{if (NF>=8) {print}}' /etc/inittab
# inittab is only used by upstart for the default runlevel.
# ADDING OTHER CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.
# Terminal gettys are handled by /etc/init/tty.conf and /etc/init/serial.conf,
# For information on how to write upstart event handlers, or how
# upstart works, see init(5), init(8), and initctl(8).
#   0 - halt (Do NOT set initdefault to this)
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   6 - reboot (Do NOT set initdefault to this)

 8.2 while,使用格式:while (condition) {while body}。

    示例:显示/etc/inittab文件中每行的奇数字段:

[root@localhost ~]# awk '{i=1;while (i<=NF){print $i;i+=2}}' /etc/inittab 
#
is
used
upstart
the
runlevel.
#
#
OTHER
HERE
HAVE
EFFECT
YOUR
#
#
initialization
started
/etc/init/rcS.conf
#
#
runlevels
started
/etc/init/rc.conf
#
#
is
by
#
#
gettys
handled
/etc/init/tty.conf
/etc/init/serial.conf,
#
configuration
/etc/sysconfig/init.
#
#
information
how
write
event
or
#
works,
init(5),
and
#
#
runlevel.
runlevels
are:
#
-
(Do
set
to
#
-
user
#
-
without
(The
as
if
do
have
#
-
multiuser
#
-
#
-
#
-
(Do
set
to
#
id:3:initdefault:

上例中每一行的内容遍历后换行,而且显示其奇数字段的方式为:

[root@localhost ~]# awk '{i=1;while(i<=NF){printf "%s ", $i;i+=2}print ""}' /etc/inittab
# is used upstart the runlevel. 
# 
# OTHER HERE HAVE EFFECT YOUR 
# 
# initialization started /etc/init/rcS.conf 
# 
# runlevels started /etc/init/rc.conf 
# 
# is by 
# 
# gettys handled /etc/init/tty.conf /etc/init/serial.conf, 
# configuration /etc/sysconfig/init. 
# 
# information how write event or 
# works, init(5), and 
# 
# runlevel. runlevels are: 
# - (Do set to 
# - user 
# - without (The as if do have 
# - multiuser 
# - 
# - 
# - (Do set to 
# 
id:3:initdefault:

显示/etc/inittab文件中每行中字符长度大于6的字段:

[root@localhost ~]# awk '{i=1;while (i<=NF){if (length($i)>=6) {print $i};i++}}' /etc/inittab
inittab
upstart
default
runlevel.
ADDING
CONFIGURATION
EFFECT
SYSTEM.
System
initialization
started
/etc/init/rcS.conf
Individual
runlevels
started
/etc/init/rc.conf
Ctrl-Alt-Delete
handled
/etc/init/control-alt-delete.conf
Terminal
gettys
handled
/etc/init/tty.conf
/etc/init/serial.conf,
configuration
/etc/sysconfig/init.
information
upstart
handlers,
upstart
works,
init(5),
init(8),
initctl(8).
Default
runlevel.
runlevels
initdefault
Single
Multiuser,
without
networking)
multiuser
unused
reboot
initdefault
id:3:initdefault:

8.3 do-while循环

格式:do {do-while body} while (condition),该循环格式会至少执行一次循环体。

8.4 for循环

格式:for (variable assignment; condition; iteration process) {for body}

示例:打印/etc/inittab文件中每行的奇数字段:

[root@localhost ~]# awk '{for (i=1;i<=NF;i+=2){printf "%s ",$i};print ""}' /etc/inittab# is used upstart the runlevel. 
# 
# OTHER HERE HAVE EFFECT YOUR 
# 
# initialization started /etc/init/rcS.conf 
# 
# runlevels started /etc/init/rc.conf 
# 
# is by 
# 
# gettys handled /etc/init/tty.conf /etc/init/serial.conf, 
# configuration /etc/sysconfig/init. 
# 
# information how write event or 
# works, init(5), and 
# 
# runlevel. runlevels are: 
# - (Do set to 
# - user 
# - without (The as if do have 
# - multiuser 
# - 
# - 
# - (Do set to 
# 
id:3:initdefault:

显示/etc/inittab文件中字段的长度大于6的字段:

[root@localhost ~]# awk '{for (i=1;i<=NF;i++){if (length($i)>=6) print $i}}' /etc/inittab
inittab
upstart
default
runlevel.
ADDING
CONFIGURATION
EFFECT
SYSTEM.
System
initialization
started
/etc/init/rcS.conf
Individual
runlevels
started
/etc/init/rc.conf
Ctrl-Alt-Delete
handled
/etc/init/control-alt-delete.conf
Terminal
gettys
handled
/etc/init/tty.conf
/etc/init/serial.conf,
configuration
/etc/sysconfig/init.
information
upstart
handlers,
upstart
works,
init(5),
init(8),
initctl(8).
Default
runlevel.
runlevels
initdefault
Single
Multiuser,
without
networking)
multiuser
unused
reboot
initdefault
id:3:initdefault:

for循环可用来遍历数组元素:

语法:for (i in array) {for body}

8.5 case语句

语法:switch (expression) {case VALUE or /RGEEXP/: statement1;... default: statementN}

8.6 循环控制

break;跳出循环

continue;提前结束本轮循环,而进入下一行

8.7 next

提前结束对本行的处理进而进入下一行的处理;

[root@localhost ~]# awk -F: '{if($3%2==0)next;print $1,$3}' /etc/passwd
bin 1
adm 3
sync 5
halt 7
operator 11
gopher 13
nobody 99
dbus 81
usbmuxd 113
vcsa 69
rtkit 499
abrt 173
postfix 89
rpcuser 29
pulse 497
[root@localhost ~]# awk -F: '{if(NR%2==0)next;print NR,$1}' /etc/passwd
1 root
3 daemon
5 lp
7 shutdown
9 mail
11 operator
13 gopher
15 nobody
17 usbmuxd
19 rtkit
21 avahi-autoipd
23 haldaemon
25 ntp
27 saslauth
29 rpcuser
31 pulse
33 tcpdump

9、数组

数组在AWK中使用方法:array[index-expression],index-expression可以使用任意字符串,是关联数组的类型。

如果某数组元素事先不存在,那么在引用时,awk会自动创建此元素并将其初始化为空串,因此,要判断某数组是否存在某元素,必须使用“index in array”这种格式,这同时也意味着AWK中数组的使用可以随时调用,无需声明,调用时即声明了该数据,如果调用数组的元素未赋初始值,那么该数组元素的初始值为空。

要遍历AWK中数组中的每一个元素,需要使用如下特殊结构:for (var in array) {for body},其中的var会遍历数组的下标,而不是元素本身。

示例1、统计tcp连接中各种状态连接的数量:

[root@localhost ~]# netstat -tan | awk '/^tcp/{++state[$NF]}END{for (s in state) {print s,state[s]}}'
ESTABLISHED 1
LISTEN 12

示例2、统计httpd日志访问文件中每个客户端IP地址对于httpd服务请求资源的次数:

[root@localhost conf.d]# awk '{ip[$1]++}END{for (i in ip) {print i,ip[i]}}' /var/log/httpd/access_log
192.168.2.1 1004

删除数组元素,语法格式:delete array[index]

10、AWK的内置函数

10.1、split对指定的字符串进行切片,以fieldsep为分隔符,切片后的结果保存至array为名称的数组中语法格式:split(string,array[,fieldsep[]]),数组下标从1开始,传统的数组从0开始,这点要注意,示例:

[root@localhost ~]# awk 'BEGIN{split("root:x:0:0",user,":");print user[1]}'
root

显示切片后的字符串保存在数组中的所有元素

[root@localhost ~]# awk 'BEGIN{split("root:x:0:0",user,":");for (i in user)print user[i]}'
0
root
x
0

10.2、length,用于计算切片后保存的数组的元素个数,示例:统计当前所有netstat连接的客户端对资源请求时IP地址出现的次数。

[root@localhost ~]#  netstat -tn | awk '/^tcp/{lens=split($5,client,":");ip[client[lens-1]]++}END{for (i in ip) print i,ip[i]}'
192.168.2.100 1

体会:AWK作为一个完整的编程语言,其具有简短的特点,学习一门语言,重要的是学习表达的语法,如同人类语言一样,都可以理解为有主谓宾定状补的概念,但在计算机语言中,不具有人类语言这样的智能性,所以书写的计算机语句中要表达完整意义语句元素与语句中其他元素的关系就需要使用其书写的位置、特殊字符等等来加以定义,使得计算机在进行语法分析时,了解其具体要表达的含义,才可能顺利的执行,所以学习计算机语言要依据计算机执行的特性,按照计算机能识别的顺序,使用相应的特定字符来表达其能识别的语句组成部分,对于书写编程语句的操作者来说,掌握计算机识别命令的语法,熟练掌握特殊字符的意义和要书写的内容可以出现在语句的位置场合等至关重要。