文本处理命令awk 实例学习参考

作者: zioer | 来源:发表于2019-06-05 13:45 被阅读0次
    awk.jpeg

    文章源地址
    awk命令是Linux系统里非常强大和功能丰富的文本行处理工具命令,使用方法简单,但是功能丰富,处理文本的速度也非常快。awk是Linux及Unix环境中现有的功能最强大的数据处理引擎之一。强大到一个命令工具拥有了自己的语言,Awk的名字来源于它的三个开发者首字母(Alfred Aho, Peter Weinberger, and Brian Kernighan), 这款软件在1977年就有了,一直保留至今足以说明其重要性了。当你遇到文本处理问题一筹莫展时,而你恰恰又看到了awk命令的使用方法介绍,那么你会在这里找到你的解决方法的。

    awk语法

    awk的基本使用方法如下:

    pattern { action }
    

    pattern 就是模式,用来精准的查找到你要处理的数据位置,支持数值判断,字符串比较,正则匹配等。
    action 就是要对 pattern 查找到的数据行进行处理的逻辑动作,例如输出找到的行数据、统计行数或者是查找是否有你的名字等等。

    awk pattern语法

    模式pattern可以是以下任何任何一种格式:

    BEGIN   : 内建模式,表示在处理文件之前的模式操作,我们可以在这个模式中执行一些初始化等预先的动作任务。
    END     : 内建模式,表示在处理文件之后的模式操作,我们可以在这个模式中执行一些展示统计结果等输出的动作任务。
    /regular expression/  : 正则表达式,支持大部分正则语法,例如 /^[0-9]/ 匹配首部包含数字的行。
    relational expression : 关系表达式,例如 大于,小于,等于的比较 `$1 > 100` 匹配 第一个字段大于100的行。
    pattern && pattern    : 模式与模式的逻辑与,两模式都真时匹配行。
    pattern || pattern    : 模式与模式的逻辑或,两模式至少一个为真即可匹配行。
    ! pattern             : 模式的逻辑非, 当模式为假时匹配行, 例如 `! $1 > 10` 匹配的是第一个字段不大于10的行数据。
    (pattern)             : 括号包含的模式,让模式表达的逻辑关系可以更复杂,例如 `($1~/^ABC/ && $2 > 100) || $1~/^XYZ/` ,匹配 第一列为"ABC"并且第二列大于100的行或者匹配第一列以XYZ开头的行,其实可以更复杂,不过太复杂就不太直观理解,能简单化尽量简单表达。
    pattern1, pattern2    :  范围模式匹配,匹配 `pattern1`和`pattern2`之间的行数据。
    pattern1 ? pattern2 : pattern3 : 三元运算,这个在Unix系统有可能不支持的,在 `gawk`和`nawk`下通常是支持的, 如果 `pattern1` 为真,则当 `pattern2`为真的行数据匹配成功,如果 `pattern1`为假,则当` pattern3` 为真时匹配行数据, 例如 `$1~/^ABC/ ? $2 > 100 : $1~/^XYZ/` ,预期类似的语法为`($1~/^ABC/ && $2 > 100) || $1~/^XYZ/` ,所以这个是可以通过其他逻辑表达的,很少用到了。
    
    

    awk 内建变量

    NR:已输入记录的条数。
    NF:当前记录中域的个数。记录中最后一个域可以以$NF的方式引用。
    FILENAME:当前输入文件的文件名。
    FS:“域分隔符”,用于将输入记录分割成域。其默认值为“空白字符”,即空格和制表符。FS可以替换为其它字符,从而改变域分隔符。
    RS:当前的“记录分隔符”。默认状态下,输入的每行都被作为一个记录,因此默认记录分隔符是换行符。
    OFS:“输出域分隔符”,即分隔print命令的参数的符号。其默认值为空格。
    ORS:“输出记录分隔符”,即每个print命令之间的符号。其默认值为换行符。
    OFMT:“输出数字格式”(Format for numeric output),其默认值为"%.6g"。
    

    awk 内建函数

    awk 脚本用法

    以一个hello.awk脚本文件为例,说明使用awk脚本方法

    1. awk脚本第一行添加#!/usr/bin/awk -f ,表示这个脚本需要使用 awk 命令进行解析执行;
    2. 创建脚本后,还需要给脚本文件增加可执行权限,命令为chmod +x ./hello.awk

    增加完可执行权限后,我们就可以像执行命令一样执行hello.awk脚本了。

    $ chmod +x hello.awk
    
    $ ./hello.awk
    Hello, world!
    
    $ cat hello.awk
    #!/usr/bin/awk -f
    BEGIN { print "Hello, world!" }
    
    

    如果我们hello.awk给脚本命名是 cat 的话,这与系统自带的 cat命令重名了,那么我们怎么知道这个是不是系统命令呢?

    $ file ./cat
    cat: awk script, ASCII text executable
    

    当然,我们可以直接查看这个cat文件内容就可以知道它是一个awk脚本了,然而对于二进制文件的时候file命令就非常有帮助啦。

    awk 实例

    awk 第一个实例

    第一个是Hello World!:

    $ echo "Bob"|awk 'BEGIN{ print "Hello "}{ print $0} END{ print "World!"}'
    Hello
    Bob
    World!
    
    • BEGIN模式是在处理所有数据之前一定要匹配的模式
    • END模式是在处理完毕所有数据之后一定要匹配的模式(常常用END进行数据展示输出)。
    • 中间的 {print $0}没有写任何模式,默认会处理所有行数据。

    awk 第二个实例:模式的使用

    awk '$1~/^[0-9]/ && NF > 1 {
      total_result += $1
    }
    END{
      printf("result: %.0f\n", total_result);
    }' a.txt
    

    awk 第三个实例:内建变量使用

    awk -F'[, ]' 'BEGIN{
      ## 设置输出域分割符号为冒号:
      OFS=":"
    }/^[0-9]/ && $1 > 1000{
      print FNR,NR,NF,FILENAME
    }' b.txt a.txt
    3:3:3:b.txt
    4:4:2:b.txt
    5:5:3:b.txt
    6:6:3:b.txt
    1:14:10:a.txt
    2:15:10:a.txt
    3:16:10:a.txt
    4:17:10:a.txt
    5:18:10:a.txt
    
    
    • BEGIN模式中设置了输出域分隔符变量OFS为“:”,之后使用print的默认域分隔符都使用OFS
    • 处理文件为两个(a.sh 和 a.txt ) , 此时对比FNRNR的差异了,NF有时候也会用来格式化过滤一些垃圾数据。

    awk 第四个实例: 逻辑判断和循环控制

    下面是一个统计英文单词频率的实例,使用两种for循环语法

    $  cat vuln.c
    #include<stdio.h>
    
    int main(int argc, char *argv[])
    {
        char buffer[500];
        strcpy(buffer, argv[1]);
        return 0;
    }
    
    $ awk -F"[^a-zA-Z]+" '{
      for(i=1; i<=NF; ++i)
      {
        words[tolower($i)]++
      }
    }
    END {
        for(i in words)
        {
          if( words[i])
          {
            print i, words[i]
          }
        }
    }' vuln.c
    argv 2
    char 2
    h 1
     13
    strcpy 1
    buffer 2
    main 1
    argc 1
    return 1
    stdio 1
    include 1
    int 2
    
    
    • for循环的语法中,我比较常用的是 for( i in words)这种, 利用哈希(hash)进行数据存储和查询使用起来非常方便,也经常用到统计分析数据中。

    awk 第五个实例: 结合Shell使用

    这个grepinawk脚本执行起来非常类似grep -H命令:

    $ cat grepinawk
    pattern=$1
    shift
    awk '/'$pattern'/ { print FILENAME ":" $0 }' $*
    
    $ ./grepinawk strcpy vuln.c
    vuln.c:    strcpy(buffer, argv[1]);
    
    $ grep -H strcpy vuln.c
    vuln.c:    strcpy(buffer, argv[1]);
    
    
    • 这个awk脚本结合了shell脚本的参数实现了类似grep命令的功能
    • grepinawk 脚本第一个参数传递给 pattern变量 , 然后通过 shift 从参数列表中删去。
    • $* 记录的是去掉了地一个参数的参数列表了,这就达到了grep 命令的简单实现。

    awk 第六个实例:awk内建函数的使用

    ## 统计一下本机都与哪些外部地址有TCP连接方法
    
    netstat -tn | awk '/ESTABLISHED/{
      LocalAddress   = $4;
      ForeignAddress = $5;
      ## 按照冒号分割 ForeignAddress 字段内容
      n = split(ForeignAddress, arr , ":")
      if( n == 2)
      {
        address[ arr[1] ] ++
      }
    }END{
      for( i in address)
      {
        printf("%-18s %-5.0f\n", i, address[ i ]);
      }
    }'
    
    
    ## 方法二
    netstat -tn | awk '/ESTABLISHED/{
      gsub(":", " ");
      i=$6;
      address[ i ]++
    }
    END{
      for( i in address)
      {
        printf("%-18s %-5.0f\n", i, address[ i ]);
      }
    }'
    
    
    • 这个实例使用了split内建函数,可以将数据重新进行拆分成一个数组,然后返回值为数组数量,需要记住,数组下表是从1开始的,并不是像C语言从0开始的。
    • 方法二通过使用 gsub 函数也实现了同样效果。

    awk 第七个实例: 重定向输出和管道的使用

    一个awk命令可能有很多输出内容,并且需要保存到不同名称的文件中,这时候就需要使用重定向输出啦:

    netstat -tn | awk '/^tcp/{
      state = $NF
      print $0 > "/tmp/test/"$NF
      printf("%-20s %-20s %-s\n", $4,$5,$6) | "sort"
    }'
    

    awk 第八个实例: 函数的使用

    >>>>>>>>>>>>$ cat b.txt
    100 200 300
    1000 40302 2130130
    12309 10201 10210
    1201 102109
    12031 102101 2101
    10201 1010 111
    10 11121
    990 1024
    4 101
    29 4010
    11 230
    5 102
    7 1021 1012 101
    
    >>>>>>>>>>>>$ awk 'function add (n1, n2) {
      return n1 + n2
    }
    $1> 100{
      printf("%-8.0f + %-8.0f = %10.0f\n", $1 , $2 , add( $1, $2))
    }' b.txt
    
    1000     + 40302    =      41302
    12309    + 10201    =      22510
    1201     + 102109   =     103310
    12031    + 102101   =     114132
    10201    + 1010     =      11211
    990      + 1024     =       2014
    
    >>>>>>>>>>>>$
    

    相关文章

      网友评论

        本文标题:文本处理命令awk 实例学习参考

        本文链接:https://www.haomeiwen.com/subject/qggjxctx.html