zoukankan      html  css  js  c++  java
  • shell学习(4)- awk

    简介

    awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。

    awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。

    awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报 表,还有无数其他的功能

    使用方法

    awk'{pattern + action}' {filenames}

    awk :对于文件中一行行的独处来执行操作 

     

    调用方法

    1.命令行方式

    awk [-F  field-separator]  'commands'  input-file(s)

    其中,commands 是真正awk命令,[-F域分隔符]是可选的。 input-file(s) 是待处理的文件。

    awk中,文件的每一行中,由域分隔符分开的每一项称为一个域。通常,在不指名-F域分隔符的情况下,默认的域分隔符是空格。

    2.shell脚本方式

    将所有的awk命令插入一个文件,并使awk程序可执行,然后awk命令解释器作为脚本的首行,一遍通过键入脚本名称来调用。

    相当于shell脚本首行的:#!/bin/sh

    可以换成:#!/bin/awk

    3.将所有的awk命令插入一个单独文件,然后调用:

    awk -f awk-script-file input-file(s)

    其中,-f选项加载awk-script-file中的awk脚本,input-file(s)跟上面的是一样的。

     

    工作流程

    awk的工作流程师这样的,读入有' '换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域, $0则表示所有域,$1表示第一个域,$n表示第n个域。 默认域分隔符是"空白键" 或 "[tab]键",所以$1表示登录用户,$3表示登录用户ip,以此类推

    1.基本用法

    只显示最近登录的5个账号

    [root@www ~]$ last -n 5 | awk'{print $1}'
    root
    root
    root
    dmtsai
    root

    只显示/etc/passwd的账户

    [root@www ~]$ cat /etc/passwd |awk  -F ':''{print $1}'  
    root
    daemon
    bin
    sys

    如果只是显示/etc/passwd的账户和账户对应的shell,而账户与shell之间以tab键分割

    [root@www ~]$ cat /etc/passwd |awk  -F ':''{print $1"	"$7}'
    root    /bin/bash
    daemon  /bin/sh
    bin     /bin/sh
    sys     /bin/sh

    搜索/etc/passwd有root关键字的所有行

    [root@www ~]$ cat /etc/passwd | awk -F':' '/root/'
    root:x:0:0:root:/root:/bin/bash
    operator:x:11:0:operator:/root:/sbin/nologin

    找/etc/passwd 以root开头的

    [yangjiao@www ~]$ cat /etc/passwd | awk -F':' '/^root/'
    root:x:0:0:root:/root:/bin/bash

    2.内置变量

    ARGC  

    命令行参数个数

    NF    

     浏览记录的域个数

    AGRV  

    命令行参数排列

    NR  

    已读的记录数   

    ENVIRON  

    支持队列中系统环境变量的使用

    OFS  

    输出域分隔符

    FILENAME  

    awk浏览的文件名  

    ORS 

    输出记录分隔符

    FNR  

    浏览文件的记录数  

    RS  

    控制记录分隔符

    FS  

    设置输入域分隔符,同- F选项

    NF    

     浏览记录的域个数

    打印每一行的行数

    [root@www ~]$ ls -l | awk '{print NR " " $1}'
    1 total
    2 drwx------
    3 drwx------+
    4 drwx------+
    5 drwx------+
    6 drwx------@
    7 drwx------+
    8 drwx------+
    9 drwx------+
    10 drwxr-xr-x+

    再理解如下应该比较简单了

    [root@www ~]$awk  -F ':'  '{printf("filename:%s,linenumber:%s,columns:%s,linecontent:%s
    ",FILENAME,NR,NF,$0)}' /etc/passwd
    filename:/etc/passwd,linenumber:1,columns:7,linecontent:root:x:0:0:root:/root:/bin/bash
    filename:/etc/passwd,linenumber:2,columns:7,linecontent:daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
    filename:/etc/passwd,linenumber:3,columns:7,linecontent:bin:x:2:2:bin:/bin:/usr/sbin/nologin
    filename:/etc/passwd,linenumber:4,columns:7,linecontent:sys:x:3:3:sys:/dev:/usr/sbin/nologin
    filename:/etc/passwd,linenumber:5,columns:7,linecontent:sync:x:4:65534:sync:/bin:/bin/sync
    filename:/etc/passwd,linenumber:6,columns:7,linecontent:games:x:5:60:games:/usr/games:/usr/sbin/nologin

    3.数组和计算

    因为awk中数组的下标可以是数字和字母,数组的下标通常被称为关键字(key)。值和关键字都存储在内部的一张针对key/value应用hash的表格里。由于hash不是顺序存储,因此在显示数组内容时会发现,它们并不是按照你预料的顺序显示出来的。数组和变量一样,都是在使用时自动创建的,awk也同样会自动判断其存储的是数字还是字符串。一般而言,awk中的数组用来从记录中收集信息,可以用于计算总和、统计单词以及跟踪模板被匹配的次数等等。

     

    [root@www ~]$ awk -F ':''BEGIN {count=0;} {name[count] = $1;count++;}; END{for (i = 0; i < NR; i++) print i, name[i]}' /etc/passwd
    0 root
    1 daemon 2 bin 3 sys 4 sync5 games

    准备测试文件

    [root@www ~]$ cat test.txt
    5555
    4444
    2222
    3333
    7777
    8888
    [root@www ~]awk '{name[x++]=$1};END{for(i=0;i<NR;i++) print i, name[i]}' test.txt
    0 5555
    1 4444
    2 2222
    3 3333
    4 7777
    5 8888

    求和

    [root@www ~]$awk '{sum+=$1} END {print "sum=",sum}' test.txt
    sum= 32219

    求平均值

    [root@www ~]$ awk '{sum+=$1} END {print "avg=",sum/NR}' test.txt
    avg= 5369.83

    求最大值

    [root@www ~]$ awk 'BEGIN {max=0} {if($1>max) max=$1 fi} END {print "MAX=",max}' test.txt
    MAX= 8888
    
    [root@www ~]$ awk 'BEGIN{max=0;print "max="max} {max=($1>max)?$1:max; print $1, "Now max is" max}' test.txt
    max=0
    5555 Now max is5555
    4444 Now max is5555
    2222 Now max is5555
    3333 Now max is5555
    7777 Now max is7777
    8888 Now max is8888

    求最小值

    [root@www ~]$ awk 'BEGIN{min=9999999}{if($1<min) min=$1} END {print "MIN=",min}' test.txt
    MIN= 2222

    统计某个文件夹下的文件占用的字节数

    [root@www ~]$ls -l | awk 'BEGIN{size=0;} {size=size+$5} END {print "size is", size}'
    size is 7729814

    4.条件操作符

        <、<=、==、!=、>=、~匹配正则表达式、!~不匹配正则表达式

        匹配:awk '{if ($4~/ASIMA/) print $0}' temp 表示如果第四个域包含ASIMA,就打印整条

        精确匹配:awk '$3=="48" {print $0}' temp    只打印第3域等于"48"的记录

        不匹配:  awk '$0 !~ /ASIMA/' temp      打印整条不包含ASIMA的记录

        不等于:  awk '$1 != "asima"' temp

        小于:    awk '{if ($1<$2) print $1 "is smaller"}' temp

        设置大小写: awk '/[Gg]reen/' temp      打印整条包含Green,或者green的记录

        任意字符: awk '$1 ~/^...a/' temp    打印第1域中第四个字符是a的记录,符号’^’代表行首,符合’.’代表任意字符

        或关系匹配: awk '$0~/(abc)|(efg)/' temp   使用|时,语句需要括起来

        AND与关系:  awk '{if ( $1=="a" && $2=="b" ) print $0}' temp

        OR或关系:   awk '{if ($1=="a" || $1=="b") print $0}' temp

    awk有三种循环: while循环;for循环;special for循环。
    
    $ awk '{ i = 1; while ( i <= NF ) { print NF,$i; i++}}' test。变量的初始值为1,若i小于可等于NF(记录中域的个数),则执行打印语句,且i增加1。直到i的值大于NF.
    
    $ awk '{for (i = 1; i<NF; i++) print NF,$i}' test。作用同上。
    
    bread continue 语句。break用于在满足条件的情况下跳出循环;continue用于在满足条件的情况下忽略后面的语句,直接返回循环的顶端。如:
    
    {for ( x=3; x<=NF; x++) 
                if ($x<0){print "Bottomed out!"; break}}
    {for ( x=3; x<=NF; x++)
                if ($x==0){print "Get next item"; continue}}
          
    next 语句从输入文件中读取一行,然后从头开始执行awk脚本。如:
    
    {if ($1 ~/test/){next}
        else {print}
    }
         
    exit 语句用于结束awk程序,但不会略过END块。退出状态为0代表成功,非零值表示出错。

        

    if 语句
    格式:
            {if (expression){
                       statement; statement; ...
                         }
            }
    $ awk '{if ($1 <$2) print $2 "too high"}' test。如果第一个域小于第二个域则打印。
    
    $ awk '{if ($1 < $2) {count++; print "ok"}}' test.如果第一个域小于第二个域,则count加一,并打印ok。

    if/else语句,用于双重判断

    格式:
            {if (expression){
                       statement; statement; ...
                           }
            else{
                       statement; statement; ...
                           }
            }

       $ awk '{if ($1 > 100) print $1 "bad" ; else print "ok"}' test。如果$1大于100则打印$1 bad,否则打印ok。

        $ awk '{if ($1 > 100){ count++; print $1} else {count--; print $2}' test。如果$1大于100,则count加一,并打印$1,否则count减一,并打印$1。

    [root@www ~]$awk 'BEGIN {BASE="7000"} {if($1<BASE) print $0}' test.txt
    5555
    4444
    2222
    3333
    [root@www ~]$ awk '{total+=$1};END{print total}' test.txt
    32219

    5.BEGIN和END模块

    BEGIN模块后紧跟着动作块,这个动作块在awk处理任何输入文件之前执行。所以它可以在没有任何输入的情况下进行测试。它通常用来改变内建变量的值,如OFS,RS和FS等,以及打印标题

    END不匹配任何的输入文件,但是执行动作块中的所有动作,它在整个输入文件处理完成后被执行。如$ awk 'END{print "The number of records is" NR}' test,上式将打印所有被处理的记录数。

    6.举个例子

    有a.txt如下

    "北京" =>1
    "上海" =>2
    "天津"=>3
    "重庆"=>4
    "河北省"=>5
    "山西省"=>6
    "内蒙古"=>7
    "辽宁省"=>8
    "吉林省"=>9
    "黑龙江省"=>10

     现把key和value互换并去掉空格,用如下命令,或者先去掉空格,用下边第二条命令

    cat a.txt | awk -F'=>'  '{printf("%s=>%s
    ",$2,$1)}' | sed 's/s+//g' > b.txt

      cat a.txt | sed 's/s+//g' | awk -F'=>' '{printf("%s => %s ",$2,$1)}' > b.txt

     修改完后的b.txt如下

    1=>"北京"
    2=>"上海"
    3=>"天津"
    4=>"重庆"
    5=>"河北省"
    6=>"山西省"
    7=>"内蒙古"
    8=>"辽宁省"
    9=>"吉林省"
    10=>"黑龙江省"

      

    7.常用例子

    cat www.slow.log.20190508 | grep -E  "08-May-2019 [^]]+" -o | awk -F "	" '{a[$1]++}END{for (i in a) print i,a[i]}'
    
    cat www.slow.log  | grep -E "09-May-2019 [0-9]+:[0-9]+:[0-9]+" -o
    
    cat forbidword.txt | awk '{a[$1]++}END{for (i in a) {if(a[i]>1)print i,a[i]}}'
    
    cat access.log.minute.201911061605 |grep mtoutiao/ads |  grep -E "ls=[^&]+" -o | awk '{a[$1]++}END{for(i in a) print i,a[i]}'

    参考文档:https://www.cnblogs.com/losbyday/p/5854707.html

                   https://www.cnblogs.com/losbyday/p/5854725.html

                  https://www.cnblogs.com/bugingcode/p/8287914.html

                  https://www.cnblogs.com/emanlee/p/3327576.html

  • 相关阅读:
    【随手记录】PG数值类型记录
    【SkyWalking】 Can't find service by service instance id from cache, service instance id is:xxx
    【随手记录】定时邮件发送限制 SMTPSendFailedException:557
    【随手记录】IDEA一个工程打开多个项目
    【随手记录】时间格式转换报错:while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSZ'
    【随手记录】实体类里方法自动执行问题
    【随手记录】IDEA的工具mode切换
    【随手记录】MagicNumber 魔数 在协议里作用
    js singleton
    js 对象的深层嵌套函数对对象属性的访问
  • 原文地址:https://www.cnblogs.com/kumufengchun/p/10156469.html
Copyright © 2011-2022 走看看