当前位置 : 主页 > 操作系统 > centos >

玩转awk

来源:互联网 收集:自由互联 发布时间:2022-06-20
简单介绍一下,awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大,这是我们玩linux的必备基本功,若要对其身世有更详尽的

   简单介绍一下,awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大,这是我们玩linux的必备基本功,若要对其身世有更详尽的了解,自行搜索即可。对于工具的知识,笔者尽量将每个知识点的简要说明,并给出实例。

简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。

用法:
awk [options] 'scripts' file1,file2...
awk [options] 'pattern {action}' file1,file2...

options是awk的支持的选项,譬如-F -v等; scripts是其处理脚本,包含模式pattern和动作action(模式和动作的关系一般为,模式负责确定有效字段,动作负责对其处理)

一、print的简单使用

创建一个简单的测试文件如下:

  • [root@mos download]# cat demo.txt  
  • Welcome to mos blog. 
  • This is a test file. 

  • 例:打印整行: $0

  • [root@mos download]# awk '{print $0}' demo.txt  
  • Welcome to mos blog.  
  • This is a test file. 
  • 例:打印每行的最后一个字段: $NF

  • [root@mos download]# awk '{print $NF}' demo.txt  
  • blog.  
  • file. 
  • 例:打印第二个字段: $2

  • [root@mos download]# awk '{print $2}' demo.txt  
  • to  
  • is 
  • 例:打印每行的倒数第二个字段,并在其后打印OK

  • [root@mos download]# awk '{print $(NF-1),"OK"}' demo.txt  
  • mos OK  
  • test OK 
  • 例:打印行号

  • [root@mos download]# awk '{print NR,$0}' demo.txt  
  • 1 Welcome to mos blog.  
  • 2 This is a test file. 
  • 例:打印当前系统环境变量的某个特定值,譬如路径,下面俩输出一样

  • [root@mos download]# awk '{print ENVIRON["USER"];}' demo.txt  
  • root 
  • root 
  • [root@mos download]# awk 'BEGIN{print ENVIRON["PATH"];}' 
  • /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin 
  • PS:后面的文件只是为了让print成功执行,BEGIN是执行前可以输出的数据,常用作写为报告标题和测试输出,其内容不能获得与文件修改操作有关的变量,后面会说到;
  • 例:awk的默认分隔符为空格,而有些文本字以其他字符为分隔符,以下两例相同

  • [root@mos download]# awk -F: {'print $1,$NF'} /etc/passwd|tail -1  
  • mos1 /bin/bash  
  • [root@mos download]# awk -v FS=: '{print $1,$NF}' /etc/passwd|head -1  
  • root /bin/bash 
  • 例:修改输出分隔符号,特殊字符需转义,如下几例:

  • [root@mos download]# awk -v OFS=. '{print $1,$NF}' demo.txt  
  • Welcome.blog.  
  • This.file.  
  • [root@mos download]# awk -v OFS="~" '{print $1,$NF}' demo.txt  
  • Welcome~blog.  
  • This~file.  
  • [root@mos download]# awk -v OFS=\" '{print $1,$NF}' demo.txt  
  • Welcome"blog.  
  • This"file.  
  • [root@mos download]# awk -v OFS=9 '{print $1,$NF}' demo.txt  
  • Welcome9blog.  
  • This9file.  
  • [root@mos download]# awk -v OFS=XXXX '{print $1,$NF}' demo.txt  
  • WelcomeXXXXblog.  
  • ThisXXXXfile.  
  • PS: 若在print输出字段的之间不写逗号,则输出分隔符无效,输出的结果会直接连在一起 
  • awk中有许多默认的变量,例如之前列出的:$1~$n、$0、FS、OFS等。下图为awk内置变量表:

  • 属性              说明  
  • $0             当前记录(作为单个变量)  
  • $1~$n          当前记录的第n个字段,字段间由FS分隔  
  • FS             输入字段分隔符 默认是空格  
  • NF             当前记录中的字段个数,就是有多少列  
  • NR             已经读出的记录数,就是行号,从1开始  
  • RS             输入的记录他隔符默 认为换行符  
  • OFS            输出字段分隔符 默认也是空格  
  • ORS            输出的记录分隔符,默认为换行符  
  • ARGC           命令行参数个数  
  • ARGV           命令行参数数组  
  • FILENAME       当前输入文件的名字  
  • IGNORECASE     如果为真,则进行忽略大小写的匹配  
  • ARGIND         当前被处理文件的ARGV标志符  
  • CONVFMT        数字转换格式 %.6g  
  • ENVIRON        UNIX环境变量  
  • ERRNO          UNIX系统错误消息  
  • FIELDWIDTHS    输入字段宽度的空白分隔字符串  
  • FNR            当前记录数  
  • OFMT           数字的输出格式 %.6g  
  • RSTART         被匹配函数匹配的字符串首  
  • RLENGTH        被匹配函数匹配的字符串长度  
  • SUBSEP        \034  
  •     声明一下:由于许多变量笔者亦不怎么常用,因此笔者此处未全部列出。 
  •  

    二、printf的使用

    格式:
        printf format1,format2..., item1,item2...
    PS:与print不同的是,首先,其多了个f;然后,其不会自动换行,而且还需要对每个字段指定输出格式。听起来很麻烦,用起来很爽的。

    例:左右对齐打印输出,中间的符号笔者自己添加的,双引号和反斜杠前使用反斜杠转义即可;

  • [root@mos download]# awk '{printf "%15s->%10s\n",$1,$NF}' demo.txt  
  •         Welcome->     blog.  
  •            This->     file.  
  • [root@mos download]# awk '{printf "%-15s#%10s\n",$1,$NF}' demo.txt  
  • Welcome        #     blog.  
  • This           #     file.  
  • [root@mos download]# awk '{printf "%-15s'\''%10s\n",$1,$NF}' demo.txt  
  • Welcome        '     blog.  
  • This           '     file.  
  •     PS:一个单引号的写法太贱了,还有%,得写三个..各种无力吐槽 
  • 例:以特定格式输出passwd字符

  • [root@mos download]# awk -F: '{printf "%-15s->%10s    ->%20s\n",$1,$(NF-3),$NF}' /etc/passwd|head -2  
  • root           ->         0    ->           /bin/bash  
  • bin            ->         1    ->       /sbin/nologin 
  • 例:以几种不同的计数方法打印一些数字

  • [root@mos ~]# awk 'BEGIN{n1=111.29;n2=-3.444;n3=5.6789;printf("%.2f,%.2u,%.2g,%X,%o\n",n1,n2,n3,n1,n1);}' 
  • 111.29,18446744073709551613,5.7,6F,157 
  • printf的格式都以%开头,后跟字符:具体如下:  

  • 格式符        说明  
  • %d        十进制有符号整数  
  • %u        十进制无符号整数  
  • %f        浮点数  
  • %s        字符串  
  • %c        显示字符的ASCII码  
  • %p        指针的值  
  • %e        科学技术法显示数值  
  • %x        %X 无符号以十六进制表示的整数  
  • %o        无符号以八进制表示的整数  
  • %g        %G 以科学计数法或浮点数的格式显示数值  
  • %%        显示其自身 
  •  
  • 修饰符:  
  • -:  左对齐     这个好使,下面俩试了试,笔者没试出啥感觉..  
  • +:  显示数值符号  
  • N: 显示 
  • 三、输出重定向
    awk的输出结果可以直接使用输出重定向和管道,其作用和命令结束再次使用重定向和管道的效果类似,笔者在此不长篇叙述,仅作简单示范和介绍:
    print items > output_file
    print items >> output_file
    print items | command

    特殊文件描述符:
    /dev/stdin:    标准输入
    /dev/sdtout: 标准输出
    /dev/stderr:  错误输出
    /dev/fd/N:    某特定文件描述符,如/dev/stdin就相当于/dev/fd/0;

    例:将结果输出到当前目录下的某个文本中;下两例效果一样

  • # awk -F: '{printf "%-15s->%+10s->%20s\n",$1,$(NF-2),$NF > "test.txt" }' /etc/passwd  
  • # awk -F: '{printf "%-15s->%+10s->%20s\n",$1,$(NF-2),$NF }' /etc/passwd > test1.txt 
  •  

    awk的操作符,这些操作符和shell编程中的意思大都一致,因此不做详细解说,又因为单单一个的实例难以表达,笔者下方列出完之后,实例部分大都混合使用,笔者会一一说明。


    一、算数操作符

  • 操作符     描述 
  • -x        负值  
  • +x        转换为数值  
  • x^y       次方  
  • x**y      次方  
  • x*y       乘法  
  • x/y       除法  
  • x+y       加法  
  • x-y       减法  
  • x%y       取余  
  • 二、字符串操作符:
    直接将print输出的结果之间不适用逗号或自定义OFS,即可,其实这是一个空值
    例如:以下两例效果一样

  • [root@mos download]# awk '{print $1 $2}' demo.txt  
  • Welcometo  
  • Thisis  
  • [root@mos download]# awk '{print $1$2}' demo.txt  
  • Welcometo  
  • Thisis  
  • 三、赋值操作符

  • 操作符     描述 
  • =         赋值操作符  
  • +=        赋值加操作符  
  • -=        赋值减操作符  
  • *=        赋值乘操作符  
  • /=        赋值除操作符  
  • %=        赋值求余操作符  
  • ^=        赋值求幂操作符  
  • **=       赋值求幂操作符  
  •     PS:需要注意的是,如果某模式为=号,此时使用/=/可能会有语法错误,应以/[=]/替代; 
  • 四、布尔值
    awk中,任何非0值或非空字符串都为真,反之就为假;
    五、比较操作符

  • 操作符     描述  
  • >         大于  
  • <         小于  
  • >=        大于等于  
  • <=        小于等于  
  • ==        等于  
  • !=        不等于  
  • ~         匹配  
  • !~        匹配取反  
  • 六、表达式间的逻辑关系符 
    && :逻辑与
    ||     :逻辑或

    例:
    打印系统上uid大于等于500且以m开头的行的首字段;&&与

  • [root@mos ~]# awk -F: '$3>=500 && $1 ~ /^m/{print $1}' /etc/passwd  
  • mos  
  • mos1  
  • 打印系统上uid大于等于500或以v开头的行的首字段;||:或 

  • [root@mos ~]# awk -F: '$3>=500 || $1 ~/^v/{print $1}' /etc/passwd  
  • vcsa  
  • nfsnobody  
  • student  
  • visitor  
  • mos  
  • mos1  
  • 打印系统上uid大于500的以v和m开头的行的首字段:

  • [root@mos ~]# awk -F: '$3>=500 && $1 ~/^m/|| $3>=500 && $1 ~/^v/{print $1}' /etc/passwd 
  • visitor  
  • mos  
  • mos1  
  • 七、条件表达式
    selector?if=true=exp:if=false=exp
    selector是条件,例如使用index函数判断一个变量中有无特定字符;有无则输出特定表达式if=true=exp:if=false=exp,如下例:

    在变量值中做查找判断,index是awk的内置函数

  • [root@mos ~]# awk 'BEGIN{msg="My name is MOS!";print index(msg,"MOS")?"ok":"no";}'  
  • ok  
  • [root@mos ~]# awk 'BEGIN{msg="My name is MOS!";print index(msg,"AAA")?"ok":"no";}'  
  • no 
  • 八、函数调用
    function_name (parament1,parament2...)
    用法
    ...您看上面,index函数的例子...对头,就那么用的。关于函数和以上的例子,在awk的模式介绍完后,基本都会有.

    awk的模式
    上面的关于逻辑运算符&&和||的例子中,已经用到了awk的模式,其以m和v开头的和大于等于500的运算,分别是表达式expression和正则表达式regexp,需要说明的是,其还支持取反和模糊匹配等,如下例及各种例:

    例:打印/etc/passwd中以root开头的相应字段和非以root开头的相应字段

  • [root@mos download]# awk -F: '/^root/{print $1,$NF}' /etc/passwd  
  • root /bin/bash  
  • [root@mos download]# awk -F: '!/^root/{print $1,$NF}' /etc/passwd|head -2  
  • bin /sbin/nologin  
  • daemon /sbin/nologin 
  • 例:第三个字段有0的打印,和仅为0的,模糊匹配和精确匹配

  • [root@mos ~]# awk -F: '$3~0{print}' /etc/passwd  
  • root:x:0:0:root:/root:/bin/bash  
  • uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin  
  • .........  
  • [root@mos ~]# awk -F: '$3==0{print}' /etc/passwd  
  • root:x:0:0:root:/root:/bin/bash 
  • 除了上面用到的表达式,还有ranges和BEGIN/END,其分别为范围匹配和特殊模式;例子如下:

    例:打印从visitor开头的行到mos1开头的行中的所有行:ranges范围匹配

  • [root@mos ~]# awk '/^visitor/,/^mos1/{print}' /etc/passwd  
  • visitor:x:501:501::/home/visitor:/bin/bash  
  • mos:x:502:502::/home/mos:/bin/bash  
  • mos1:x:503:503::/home/mos1:/bin/bash 
  • 例:打印结果添加标题和结束语:BEGIN和END的使用

  • [root@mos download]# awk -F: 'BEGIN {print "Name\tShell"}{print $1,"\t",$NF}END{print "---End---"}' /etc/passwd 
  • Name    Shell 
  • root     /bin/bash 
  • ................
  • mysql    /bin/bash 
  • ---End--- 
  • 例:在条件执行之前,先打印标题字段,并在结束后,打印结束字段

  • [root@mos ~]# awk -F: 'BEGIN{print "Username    UID"}$3>=500 && $1 ~ /^m/{printf "%-15s%s\n",$1,$3}END{print "---END---"}' /etc/passwd  
  • Username    UID  
  • mos            502  
  • mos1          503  
  • ---END---  
  • [root@mos ~]# awk -F: 'BEGIN{print "Username    UID"}{printf "%-15s%s\n",$1,$3}END{print "---END---"}' /etc/passwd    去掉条件匹配,打印所有. 
  • 例:统计有某个关键字的行的总行数,并输出BEGIN和END信息,其中count++表示:每遇一个相同的则加1,最后输出总数

  • [root@mos download]# awk -F: 'BEGIN {count=0}$NF ~/nologin/ {count++}END{print "Nologin number is:",count}' /etc/passwd 
  • Nologin number is: 30 
  • 例:使用BEGIN也可以直接显示字符串

  • [root@mos ~]# awk  'BEGIN{print "a""b"}'  
  • ab  
  • [root@mos ~]# awk  'BEGIN{print "a","b"}'  
  • a b  
  • [root@mos ~]# awk -v OFS=\~ 'BEGIN{print "a","b"}'  
  • a~b 
  • 例:BEGIN也可以给字段分隔符变量赋值

  • [root@mos download]# awk 'BEGIN{FS=":"}{print $1}' /etc/passwd|head -2 
  • root 
  • bin 
  • 例:打印判断系统用户和普通用户输出:if条件判断和多条件判断

  • [root@mos download]# awk -F: '{if ($3<=499)print $1,"Sys User";else print $1,"Common User."}' /etc/passwd  
  • root Sys User 
  • ........... 
  • mos1 Common User
  • mysql Sys User 
  • [root@mos download]# awk -F: '{if($3==0){print $1,"Admin User";}else if($3>0 && $3<=499){print $1,"Sys User";}else{print $1,"Comm User"}}' /etc/passwd 
  • root Admin User 
  • bin Sys User 
  • ............. 
  • nfsnobody Comm User 
  • ............ 
  • oprofile Sys User 
  • .................... 
  • mos1 Comm User 
  • mysql Sys User 
  • 例:使用while打印/etc/passwd中非root且非空行的前四个字段:while语句

  • [root@mos download]# awk -F: '$1!~/root/ && $1 !~/^$/{i=1;while(i<=4){print $i;i++}}' /etc/passwd|head -4 
  • bin 
  • 例:打印不包含root行的每行的奇数和偶数字段:while和for循环

  • # awk -F: '$1!~/root/{i=1;while(i<=NF){print $i;i+=2}}' /etc/passwd    while奇  
  • # awk -F: '$1!~/root/{i=2;while(i<=NF){print $i;i+=2}}' /etc/passwd  while偶  
  • # awk -F: '{for(i=1;i<=NF;i+=2) print $i}' /etc/passwd        for奇  
  • # awk -F: '{for(i=2;i<=NF;i+=2) print $i}' /etc/passwd        for偶 
  • 例:数组的引用,下标可以是字符串,但需要双引号,数字则不需要

  • [root@mos download]# awk 'BEGIN{A[10]="hello";A[15]="world";print A[10],A[15]}' 
  • hello world 
  • [root@mos download]# awk 'BEGIN{A["m"]="hello";A["n"]="world";print A["m"],A["n"]}' 
  • hello world 
  • 例:数组的遍历

  • [root@mos download]# awk 'BEGIN{A["m"]="hello";A["n"]="world";for(B in A)print A[B],"OK"}' 
  • hello OK 
  • world OK 
  • 例:遍历环境变量关联数组,并以其下标加等号打印出:数组和环境变量

  • [root@mos download]# awk 'BEGIN{for(k in ENVIRON){print k"="ENVIRON[k],"OK";}}' 
  • AWKPATH=.:/usr/share/awk OK 
  • OLDPWD=/root OK 
  • .........
  • 例:统计以tcp开头网络状态的连接数:模式匹配、for循环和数组

  • [root@mos download]# netstat -ant|awk '$1~/tcp/{S[$NF]++}END{for (A in S) printf "%-15s:%s\n",A,S[A]}' 
  • LISTEN         :11 
  • ESTABLISHED    :2 
  • 例:统计不为空的用户的shell数量

  • [root@mos download]# awk -F: '$NF!~/^$/{S[$NF]++}END{for (A in S)print A,S[A]}' /etc/passwd 
  • /bin/sync 1 
  • /bin/bash 6 
  • /sbin/nologin 30 
  • /sbin/halt 1 
  • /sbin/shutdown 1 
  • 例:打印100以内的加法,使用while和do~while

  • [root@mos download]# awk 'BEGIN{res=0;i=0;do{res+=i;i++;}while(i<=100)print res;}' 
  • 5050 
  • [root@mos download]# awk 'BEGIN{while(i<=100){res+=i;i++;}print res;}'  
  • 5050 
  • 例:awk执行计算的速度是shell的近40倍   

  • [root@mos ~]# time(awk 'BEGIN{res=0;for(i=0;i<=10000;i++){res+=i;}print res;}')  
  • 50005000  
  • real    0m0.003s  
  • user    0m0.001s  
  • sys    0m0.001s  
  • [root@mos ~]# time(res=0;for i in $(seq 10000);do res=$(($res+i));done;echo $res;)  
  • 50005000  
  • real    0m0.114s  
  • user    0m0.097s  
  • sys    0m0.011s 
  • 例:统计apache常规日志的IP的访问次数,最多的前5个

  • # awk '{IP[$1]++}END{for(A in IP)print IP[A],A}' access_log|sort -rn|head -5 
  •  

    awk一些内部函数的使用

    例:取1000以内的随机数

  • [root@mos download]# awk 'BEGIN{srand();fr=int(1000*rand());print fr;}' 
  • 796 
  • [root@mos download]# awk 'BEGIN{srand();fr=int(1000*rand());print fr;}' 
  • 224 
  • 例:gsub的使用,将lab变量的任意数字替换为--demo--后并输出

  • [root@mos ~]# awk 'BEGIN{lab="2My name is MOS2012.";gsub(/[0-9]+/,"--demo--",lab);print lab}'  
  • --demo--My name is MOS--demo--. 
  • 例:正则表达式匹配,match函数的使用

  • [root@mos download]# awk 'BEGIN{res="My name is MOS!";print match(res,/[0-9]+/)?"ok":"no";}' 
  • no 
  • [root@mos download]# awk 'BEGIN{res="My name is MOS88!";print match(res,/[0-9]+/)?"ok":"no";}' 
  • ok 
  • 例:截取字符串,substr的使用,从第四个字符,截取四个字符长度

  • [root@mos ~]# awk 'BEGIN{info="My Name is MOS!";print substr(info,4,4);}'  
  • Name 
  • 例:字符串分割,split和length的使用,先打印总长度,然后打印每个数组中每个值和其下标

  • [root@mos ~]# awk 'BEGIN{test="My name is MOS.";split(test,Ary," ");for(l in Ary){print l,Ary[l];}print length(Ary),"Str Len";}'  
  • 4 MOS.  
  • 1 My  
  • name  
  • is  
  • 4 Str Len 
  • 例:打开外部文件,close函数的用法,getline打开管道,将读取内容送给print

  • [root@mos ~]# awk -v FS=: 'BEGIN{while("cat /etc/passwd"|getline){print $1;};close("/etc/passwd");}'|head -3  
  • root  
  • bin  
  • daemon 
  • 例:逐行读取外部文件,getline用法,其实上一个也列出了getline的使用方法.

  • [root@mos ~]# awk 'BEGIN{while(getline<"/etc/passwd"){print $0,"OK";};close("/etc/passwd");}'|tail -3  
  • mos:x:502:502::/home/mos:/bin/bash OK  
  • mos1:x:503:503::/home/mos1:/bin/bash OK  
  • mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/bash OK 
  • 例:调用外部应用程序

  • [root@mos download]# awk 'BEGIN{D=system("df -hP");print D;}' 
  • Filesystem            Size  Used Avail Use% Mounted on 
  • /dev/mapper/My_vg-lv_root  9.7G  3.1G  6.2G  33% / 
  • /dev/mapper/My_vg-lv_home  496M   19M  452M   4% /home 
  • /dev/sda1              99M   13M   82M  13% /boot 
  • tmpfs                1014M     0 1014M   0% /dev/shm 
  • /dev/hdc              3.3G  3.3G     0 100% /rhel_dvd 
  • 例:获取shell变量

  • [root@mos ~]# Out="My name is MOS."  
  • [root@mos ~]# echo|awk '{print res}' res="$Out"  
  • My name is MOS.  
  • [root@mos ~]# echo|awk -v res1="$Out" 'BEGIN{print res1}'  
  • My name is MOS.  
  • [root@mos ~]# echo|awk -v res1="$Out" '{print res1}'  
  • My name is MOS. 
  • 例:时间函数mktime、strftime和system的使用:输出一个指定时间,两个时间差,指定时间跟当前时间差

  • [root@mos download]# awk 'BEGIN{Tim=mktime("2012 12 21 23 59 59");print strftime("%c",Tim);}'  
  • Fri 21 Dec 2012 11:59:59 PM CST 
  • [root@mos download]# awk 'BEGIN{Time=mktime("2012 12 21 23 59 59");Time1=mktime("2012 12 21 23 59 01");print Time-Time1;}' 
  • 58 
  • [root@mos download]# awk 'BEGIN{Time=mktime("2012 12 21 23 59 59");Time1=systime();print Time1-Time;}' 
  • 535294 
  •  

    到此为止,以上笔者缩写,若都可以理解,并自己改写成所需要的,基本上awk的常规使用,应是没问题了,当然,笔者亦未全部说完相关知识点,每个知识,若要学透,皆非朝夕之事,一起努力吧!

    网友评论