awk实例讲解说明

简述:

    awk是一种编程语言,用于在linux/unix下对文本和数据进行扫描与处理。数据可以来自标准输入、文件、管道。

    awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Peter Weinberger、Brian Kernighan。

    awk自诞生以来存在很多版本,由最初的awk、nawk、POSIX awk,到最新的gawk等。目前Linux中使用的是gawk,awk其实是gawk的一个软连接,实际上还是用的gawk。

    gawk是GNU Project 的awk解释器的开源代码实现。
    本文将以gawk作为讲解工具。
原理:
    1). awk逐行扫描文件,从第一行到最后一行,寻找匹配特定模式的行,并在这些行上进行你想要的操作。
    2). awk基本结构包括模式匹配(用于找到要处理的行)和处理过程(即处理动作)。
       pattern  {action}

  # 提示:awk读取文件内容的每一行时,将对比改行是否与给定的模式相匹配,如果匹配则执行处理过程,否则对该行不做任何处理。
如果没有指定处理脚本,则把匹配的行显示到标准输出,即默认处理动作是print打印行;
如果没有指定模式匹配,则默认匹配所有数据。

      3). awk有两个特殊的模式:BEGIN和END,他们被放置在没有读取任何数据之前以及在所有数据读取完成以后执行。

    下图是AWK编程模型


用法

    gawk [options] 'program' file file ...

        program: PATTERN { ACTION STATEMENT }

        程序:模式 { 动作 声明 }

        ACTION: print,printf          

    常用选项:

        -F[]:指明输入字段分隔符

        -v var=var:变量赋值

    

    print与printf   

        其中print函数的参数可以是变量、数值或者字符串。字符串必须用双引号引用,参数用逗号分隔。如果没有逗号,参数就串联在一起而无法区分。这里,逗号的作用与输出文件的分隔符的作用是一样的,只是后者是空格而已。printf函数,其用法和c语言中printf基本相似,可以格式化字符串,输出复杂时,printf更加好用,代码更易懂。

 

1、awk中的记录、域和输出命令print

    awk认为输入文件是结构化的,awk将每个输入文件行定义为记录,行中的每个字符串定义为域,域之间用空格、TAB键或其他符号进行分隔,分隔域的符号叫分隔符。

    awk定义域操作符$来指定执行动作的域,域操作符$后面跟数字或变量来表示域的位置,每条记录的域从1开始编号,如$1表示第1个域、$2表示第2各域、$0表示所有的域。

    新建名为sturecord的文件作为输入文件,这个文件的每个记录是学生名字、所在学校和电话号码,sturecord文件的记录、域及域分隔符如下所示:

    

    sturecord文件打印次序为2,1,3,4   如下图

       

语法:

    print item1,item2,...

    要点:

        1、各item1间使用逗号分隔,而输出时则使用输出分隔符分隔;

        2、输出的各item2可以字符串或数值、当前记录的字段($n)、变量或awk的表达式;数值会被隐式转换为字符进行输出;

        3、print后面的item如果省略,相当于print $0;如果需要输出为“空白”,使用print "";

 

2、变量

    1)内置变量

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

            RS:记录分隔符,默认为换行符;

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

            ORS:输出记录分隔符,默认为换行符;

            NF:当前行的字段数;

            NR:行数,所有文件统一计数;

            FNR:行数,各文件分别计数;

            FILENAME:当前文件名;

            ARGC:命令行的参数的个数;

            ARGV:数组,保存了命令行参数;

    2)自定义变量

            -v var=var;

                    变量名区分字符大小写

            定义变量的位置

                    1、可以在program(程序)中定义变量;

                    2、通过-v选项定义变量;

    例如:

  1. passwd文件内容如下
    [root@null ~]# cat 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
  1. 例1:FS变量
    [root@null ~]# awk -F:'{print $1,$3}' passwd #-F指定分隔符: 显示如下
    root 0
    bin 1
    daemon 2
    adm 3
    lp 4
    [root@null ~]# awk -v FS=":"'{print $1,$3}' passwd #-v 指定变量的FS分隔符
    root 0
    bin 1
    daemon 2
    adm 3
    lp 4
  1. 例2:RS变量
    [root@null ~]# awk -v RS=":"'{print $1,$3}' passwd #指定换行符为:,文件一遇到:就换行
    root
    x
    0
    0
    root
    /root
    /bin/bash
    ....# 以下省略
  1. 例3:OFS变量
    [root@null ~]# awk '{print $1,$3}' 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
    [root@null ~]# awk -F:-v OFS="============"'{print $1,$3}' passwd #指定输出分隔符为============
    root============0
    bin============1
    daemon============2
    adm============3
    lp============4
  1. 例4:ORS变量
    [root@null ~]# awk -F:-v ORS="--"'{print $1,$3}' passwd #指定换行符为 -- 当然也可以为空
    root 0--bin 1--daemon 2--adm 3--lp 4--[root@null ~]#
    [root@null ~]# awk -F:-v ORS="--"'{print $1,$3}' passwd #指定换行符为空的时候
    root 0--bin 1--daemon 2--adm 3--lp 4--[root@null ~]#
  1. 例5:NF、NR、FNR、FILENAME的值
    [root@null ~]# awk -F:'{print NF }' passwd #变量NF,当前行的字段数。在awk里变量引用不需要加$
    7
    7
    7
    7
    7
  1. 例6:ARGC、ARGV的值
    [root@null ~]# awk '{print ARGC }' passwd # ARGC命令行的参数的个数,注意:命令awk也算是一个参数
    2
    2
    2
    2
    2
    [root@null ~]# awk '{print ARGV[0] }' passwd # 数组,保存了命令行参数;[]显示命令行第几个参数
    awk
    awk
    awk
    awk
    awk
    [root@null ~]# awk '{print ARGV[1]}' passwd
    passwd
    passwd
    passwd
    passwd
    passwd
  1. 例7:自定义变量(变量名需要区分字符大小写)
    [root@null ~]# awk '{file="passwd";print file,$1}' passwd #定义file值为passwd,并打印file变量,和$1的值
    passwd root:x:0:0:root:/root:/bin/bash
    passwd bin:x:1:1:bin:/bin:/sbin/nologin
    passwd daemon:x:2:2:daemon:/sbin:/sbin/nologin
    passwd adm:x:3:4:adm:/var/adm:/sbin/nologin
    passwd lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
3、printf命令
        格式:printf format,item1,item2,...
        要点:
                1、format(格式)是必须的;
                2、不会自动换行,需显示给定行分隔符;
                3、format中需要分别为后面的每个item指定一个格式符;
        格式符:都以%开头,后跟一个字符
                %c:显示字符的ASCII码;
                %d,%i:显示十进制整数;
                %e,%E:科学计数法显示数值
                %f:显示为浮点数
                %g,%G:以科学计数法格式或浮点数格式显示数值;
                %s:字符串
                %u:无符号的整数
                %%:显示%自身
        修饰符:
                #[.#]:第一个#指定显示宽度,第二个#表示小数点后的精度;(#用数字替代)
                -:左对其
                +:显示数值符号
  1. 例1:格式符
    [root@null ~]# awk 'BEGIN{printf "%c","a"}'#%c显示ASCII码,但这里显示的是字符本身,换成数字就明显了,无法复制出来
    a[root@null ~]#
    [root@null ~]# awk 'BEGIN{printf "%d",6}'#显示为十进制整数,字母都会以数值0表示,浮点数也会四舍五入变成整数
    6[root@null ~]#
    [root@null ~]# awk 'BEGIN{printf "%f\n",6.0001}'#要显示小数点后的数值需用到%f,\n是换行符,只小数点前后小数点后加起来8个不足用0表示(小数点在这里也算一个字符)
    6.000100
    [root@null ~]# awk 'BEGIN{printf "%e\n",600013.24165431}'#%e,%E:科学计数法显示数值
    6.000132e+05
    [root@null ~]# awk 'BEGIN{printf "%g\n",600013111111.24165431}'#%g,%G:以科学计数法格式或浮点数格式显示数值;
    6.00013e+11
  1. 例2:修饰符
    [root@null ~]# awk 'BEGIN{printf "%30s\n","abcd"}'
    abcd #在字符前隔着30个字符
    [root@null ~]# awk 'BEGIN{printf "%-30s\n","abcd"}'# -代表左对其
    abcd
    [root@null ~]# awk -F:'{printf "%20s:%5s\n",$1,$3}' passwd # $1显示30个字符,$3显示5个字符
    root:0
    bin:1
    daemon:2
    adm:3
    lp:4
    [root@null ~]# awk 'BEGIN{printf "%40.9f\n",45678947.1534564654}'#第一个#指定显示宽度,第二个#表示小数点后的精度(小数点后面是9个字符宽度);(#用数字替代)
    45678947.153456464
4、操作符
        算术操作符:
                +,-,*,/,^,%
                -x:负值
                +x:转换为数值
        字符串操作:字符串连接
 
        赋值操作符:
                =,+=,-=,*=,/=,^=,%=
                ++,--
        比较操作符:
                >,>=,<,<=,==,!=
        模式匹配符:
                ~
                !~
        逻辑操作符:
                &&
                ||
        条件表达式:
                selector?if-true-expression:if-false-expression
函数调用:
function_name(argu1,argu2)
  1. 例1:条件表达式
    [root@null ~]# awk -F:'{$3>=1?usertype="system user":usertype="root user";printf "%20s:%-s\n",$1,usertype}' passwd #判断变量$3是否大于或等于1,大于或等于1的话,usertype的值就等于system user,否者就等于root user,打印$1的距离为20s,usertype的值左对其$1,并且换行,然后显示$1和变量usertype
    root:root user
    bin:system user
    daemon:system user
    adm:system user
    lp:system user
5、PATTERN
        1、/regular expression/:仅处理能够被/regular expression/所匹配到的行;
        2、relational expression:关系表达式,有真假之分,一般来说,其结果为非0或为非空字符串时为"真",否则,为"假";
        3、line ranges:行范围,类似sed或vim的地址定界法; startline,endline
        4、BEGIN/END:特殊模式
                仅在awk运行程序之前执行一次(BEGIN)或仅在awk运行程序之后执行一次(END);
        5、empty:空模式,匹配任意行;
  1. [root@null ~]# awk -F:'/^\<root\>/{print $0}' passwd #仅处理能够被/regular expression/所匹配到的行;
    root:x:0:0:root:/root:/bin/bash
    [root@null ~]# awk -F:'$3>=1{print $0}' passwd #打印出所有$3大于或等于1的条目,关系表达式
    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
    [root@null ~]# awk -F:'$5~/root/{print $0}' passwd #匹配$5中有root字段的条目,模式匹配符
    root:x:0:0:root:/root:/bin/bash
    [root@null ~]# awk -F:'$7~/bash\>/{print $0}' passwd #匹配$7以bash结尾的条目,模式匹配符
    root:x:0:0:root:/root:/bin/bash
    [root@null ~]# awk -F:'BEGIN{print "username shell"}{print $1,$7}' passwd #在处理之前由BEGIN处理一段username shell添加在前面,然后处理后续部分
    username shell
    root /bin/bash
    bin /sbin/nologin
    daemon /sbin/nologin
    adm /sbin/nologin
    lp /sbin/nologin
    [root@null ~]# awk -F:'BEGIN{print "username shell\n-----------------"}{print $1,$7}END{print "----------------\nEND"}' passwd #BEGIN与END
    username shell
    -----------------
    root /bin/bash
    bin /sbin/nologin
    daemon /sbin/nologin
    adm /sbin/nologin
    lp /sbin/nologin
    -----------------
    END
6、控制语句
if (condition) statement [ else statement ]
while (condition) statement
do statement while (condition)
for (expr1; expr2; expr3) statement
for (var in array) statement
break
continue
delete array[index]
delete array
exit [ expression ]
{ statements }
 
6.1 if-else
语法:if (condition) statement [ else statement ]
if (condition) { statement; } [ else { statement; }]
用法:对awk取得的整行或行中的字段做条件判断;
  1. [root@null ~]# awk -F:'{if ($3>=1) {print $1,"is a system user."} else {print $1 " is a root user."}}' passwd # if判断$3是否大于或等于1,如果条件满足就显示 $1,"is a system user.",如果条件不满足就显示$1 " is a root user.
    root is a root user.
    bin is a system user.
    daemon is a system user.
    adm is a system user.
    lp is a system user.
6.2 while循环
语法:while (condition) statement
while (condition) { statements }
条件为真时进行循环,直到为假退出;
用法:通常用于在当前行的各字段间进行循环;
  1. 用while循环判断每个单词出现的个数
    [root@null ~]# awk -F:'{i=1;while(i<=NF){printf "%20s:%d\n",$i,length($i); i++}}' passwd
    root:4
    x:1
    0:1
    0:1
    root:4
    /root:5
    .......
6.3 do-while循环
语法:do statement while (condition)
do { do-while-body } while (condition)
意义:至少执行一次循环体;
6.4 for循环
语法:for (expr1;expr2;expr3) statement
for (expr1;expr2;expr3) { statement }
 
for (varaiable assignment; condition; iteration process) { for-body }
for循环在awk中有一个专用于遍历数组元素;
语法:for (var in array) { for-body }
  1. 用for循环判断每个单词出现的个数
    [root@null ~]# awk -F:'{for(i=1;i<=NF;i++) {printf "%s:%d\n", $i,length($i)}}' passwd
    root:4
    x:1
    0:1
    0:1
    root:4
    /root:5
    /bin/bash:9
6.5 switch
语法:switch (expression) {case VALUE or /REGEXP/:statement; ...; default; statementN}
6.6 break and continue
break [n]:退出当前循环
continue:提前结束本轮循环,直接进入下轮循环
6.7 next
提前结束对本行的处理而进入下一行的处理
  1. [root@null ~]# awk -F:'{if($3%2!=0) next; print $1,$3}' passwd #if判断$3的值对2取模,等于0就进入下一轮就是打印出来,不复合就不让进入下一行
    root 0
    daemon 2
    lp 4
7、Array(数组)
关联数组:array[index-expression]     (数组[索引表达式])
 
                    index-expression:
可以使用任意字符串;
如果某数组元素事先不存在,在引用时,awk会自动创建此元素并将其值初始化为空串;
因此,若要判断数组是否存在某元素,要使用"index in array(索引阵列)" 进行;
 
a1[mon]="Monday"
print a1[mon]
要遍历数组中的每个元素,使用:for(var in array) { for body }
注意:var会遍历array的每一个索引,print array[var]
  1. [root@null ~]# cat awk.txt #awk.txt文件中的内容
    how are you ? how do you do?
    what are you doing?
    [root@null ~]# awk '/^how/{for(i=1;i<=NF;i++) {count[$i]++}}END{for(j in count) {print j,count[j]}}' awk.txt 显示每个单词出现的次数
    are 1
    do2
    how 2
    ?2
    you 2
8、函数
8.1 内置函数
数值处理:
rand():返回0和1之间的随机数
字符串处理:
length([s]):返回指定字符串的长度
sub(r,s [,t]):以r所表示的模式来查找t字符串中的匹配,将其第一次出现替换同s所表示的字符串;
gsub(r,s [,t]):以r所表示的模式来查找t字符串中的匹配,将其所有的出现均替换同s所表示的字符串;
split(s,a [, r]):以r为分隔符切割字符串s,并将切割的结果保存至a表示数组中;
substr(s,i [,n]):从s表示的字符串中取字串,从i开始,取n个字符;
时间类的函数:
systime():取时间戳
位运算函数:
and(v1,va2);
8.2 自定义函数:
function f_name(p,q)
{
...
}
  1. 统计当前连接状态
    [root@null ~]# netstat -tan | awk '/^tcp/{len=split($5,client,":");ip[client[len-1]]++}END{for(i in ip){print i,ip[i]}}'
    7
    172.16.34.2342
    0.0.0.07
posted @ 2014-12-27 22:58  此时  阅读(402)  评论(0编辑  收藏  举报