awk等文本分析工具学习

140 阅读5分钟

背景

排查线上问题、统计脏数据时,需要从几百条日志中提取数据的主键ID。

正则表达式

需要了解的:

  1. 特殊字符:是指有特殊含义的字符,若要匹配特殊字符需要“转义”。

  1. 限定符:用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。

详见:菜鸟教程

awk文本分析语言学习

参考博客

wangchujiang.com/linux-comma…

www.manongjc.com/detail/27-d…

awk脚本基本结构

awk 'BEGIN{ print "start" } pattern{ commands } END{ print "end" }' file

一个awk脚本通常由3部分组成

  1. BEGIN语句块
  2. 能够使用 模式匹配 的通用语句块
  3. END语句块

这三个部分都是可选的,任意一个部分都可以不出现在脚本中,脚本通常是被 单引号 括起来,例如:

awk 'BEGIN{ i=0 } { i++ } END{ print i }' filename

awk 'BEGIN{ print "start" } pattern{ commands } END{ print "end" }' file

脚本中pattern(模式)详解

pattern用于过滤读取的行,它是一个表达式,可以是:

  • 正则表达式,若该行匹配上了正则表达式则执行{ commands }进行处理,否则该行被过滤掉、不处理

  • 条件表达式,若返回true则执行{ commands }进行处理,否则该行被过滤、不处理

awk的工作原理

awk 'BEGIN{ commands } pattern{ commands } END{ commands }'
  • 第一步:执行BEGIN{ commands }语句块中的语句;

  • 第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{ commands }语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕

    每行都会执行pattern{ commands }

  • 第三步:当读至输入流末尾时,执行END{ commands }语句块。

BEGIN语句块 在awk开始从输入流中读取行 之前 被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中。

END语句块 在awk从输入流中读取完所有的行 之后 即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块。

pattern语句块 中的通用命令是最重要的部分,它也是可选的。如果没有提供pattern语句块,则默认执行 { print } ,即打印每一个读取到的行,awk读取的每一行都会执行该语句块。

知识图谱(待完善)

  • 语法:条件语句、循环语句、数组

  • 内置变量

  • 内置函数

关键、核心知识

  1. awk的脚本结构

  2. 模式操作。awk的程序指令由模式和操作组成,即Pattern { Action }的形式,如果省略Action,则默认执行 print $0 的操作

    如:
    gawk '/^Error 2022-12-/{match($0,/ID:([1-9][0-9]+) ObjectID:/,a);if(length(a[1])>0){print a[1]}}'。
    pattern为/^Error 2022-12-/,用于匹配以"Error 2022-12-"字符串开头的行。

  • 数组

  • match

match

有两种:

  1. match(string,regexp):2个参数:待匹配的字符串、正则表达式

    在 string 参数指定的字符串(regexp 参数指定的扩展正则表达式出现在其中)中返回位置(字符形式),从 1 开始编号,或如果 regexp 参数不出现,则返回 0RSTART 特殊变量设置为返回值。RLENGTH 特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1

  2. match(string,regexp,array):3个参数:待匹配的字符串、正则表达式、数组

    match(s, r, a)说明:
    如果提供数组a,则清除a,然后用s中与r中带括号的子表达式 匹配的部分填充元素1到n(即,匹配的子表达式下标从1开始) 。a的第0个元素s中与整个正则表达式r匹配的部分。下标 a[n, "start"] 和 a[n, "length"] 分别提供字符串中的起始索引每个匹配子字符串的长度

备注:

Mac自带的awk只能使用match(string,regexp),需要下载gawk,就能使用match(string,regexp,array)

match(string,regexp,array)可以更方便地提取信息,在正则表达式regexp中使用()括号可以定义子表达式,然后就可以提取子表达式匹配的内容了。


例子

echo 'abc123' | awk '{i=match($0,/[0-9]+/);print i,RSTART,RLENGTH}'
输出结果:4 4 3

echo 'abc123qwe' | gawk '{i=match($0,/([0-9]+)([a-z]+)/,a);print i,RSTART,RLENGTH,a[0],a[1],a[2],a[0,"start"],a[1,"start"],a[2,"start"],a[0,"length"],a[1,"length"],a[2,"length"]}'
输出结果:4 4 6 123qwe 123 qwe 4 4 7 6 3 3

博客学习:

www.cnblogs.com/timeisbigge…

打印输出

博客学习:blog.csdn.net/weixin_4276…

  • print:会换行

  • printf:不会换行

  • 可以重定向输出

去重

博客学习:blog.csdn.net/feng973/art…

准备t1.txt文件,内容如下:

1
2
1
2
3
2
3
6
7
8
8

执行如下命令进行去重,打印去重结果:

awk ' !a[$0]++ ' t1.txt
输出结果:
1
2
3
6
7
8

!a[$0]++pattern,会过滤行。

原理:

在awk中,对于未初始化的数组变量,在进行数值运算的时候,会赋予初值0因此a[$0]=0,++运算符的特性是先取值,后加1。对于元素item,第一次出现时 a[item]=0!0true,且++后a[item]>0;后续,元素item重复出现时, a[item]>0,此时!a[item]false重复元素就被过滤掉了

实战

  1. 准备日志文件log.txt,内容如下(模拟数据):
Error 2022-12-05 10:00:01.123 msg=XXXXXXX ID:123 ObjectID:123 testtesttestXXXXxxxxx
Error 2022-12-05 10:00:01.123 msg=XXXXXXX ID:123 ObjectID:123 testtesttestXXXXxxxxx
Error 2022-12-05 10:00:01.123 msg=XXXXXXX ID:123 ObjectID:123 testtesttestXXXXxxxxx
Error 2022-12-05 11:00:02.123 msg=XXXXXXX ID:456 ObjectID:456 testtesttestXXXXxxxxx
Error 2022-12-05 11:00:02.123 msg=XXXXXXX ID:456 ObjectID:456 testtesttestXXXXxxxxx
testtesttest find query update testteste hhtp1231
Error 2022-12-05 11:00:02.123 msg=XXXXXXX ID:456 ObjectID:456 testtesttestXXXXxxxxx
testtesttest find query update testteste hhtp1231
Error 2022-12-06 12:00:03.123 msg=XXXXXXX ID:789 ObjectID:789 testtesttestXXXXxxxxx
testtesttest find query update testteste hhtp1231
testtesttest find query update testteste hhtp1231
testtesttest find query update testteste hhtp1231
Error 2022-12-07 13:00:04.123 msg=XXXXXXX ID:101112 ObjectID:101112 testtesttestXXXXxxxxx
Error 2022-12-07 13:00:04.123 msg=XXXXXXX ID:101112 ObjectID:101112 testtesttestXXXXxxxxx
Error 2022-12-07 13:00:04.123 msg=XXXXXXX ID:101112 ObjectID:101112 testtesttestXXXXxxxxx
Error 2022-12-08 14:00:05.123 msg=XXXXXXX ID:121314 ObjectID:121314 testtesttestXXXXxxxxx
testtesttest find query update testteste
testtesttest find query update testteste
Error 2022-12-09 15:00:06.123 msg=XXXXXXX ID:151617 ObjectID:151617 testtesttestXXXXxxxxx
  1. 执行如下命令,提取日志中的主键ID到testID.txt文件:
gawk '/^Error 2022-12-/{match($0, /ID:([1-9][0-9]+) ObjectID:/, a);if(length(a[1])>0){print a[1]}}' log.txt > testID.txt

得到的testID.txt文件内容如下:

123
123
123
456
456
456
789
101112
101112
101112
121314
151617
  1. 执行如下命令,对testID.txt文件中的ID去重:
awk '!a[$0]++' testID.txt > testIDDup.txt

得到的testIDDup.txt文件内容如下:

123
456
789
101112
121314
151617
  1. 执行如下命令,逗号连接testIDDup.txt文件中的所有ID得到testIDStr.txt
awk '{printf $0","}' testIDDup.txt > testIDStr.txt

得到的testIDStr.txt文件内容如下:

123,456,789,101112,121314,151617,
  1. 逗号分隔testIDStr.txt文件内容,打印每个ID:
awk -F, '{for(i=1;i<NF;i++){print $i}}' testIDStr.txt

统计行数:
awk -F, '{for(i=1;i<NF;i++){print $i}}' testIDStr.txt | wc -l

常用脚本

// 遍历每行,逗号分隔+不换行输出
awk '{printf $0","}'

// 遍历每行,双引号+逗号分隔+不换行输出
awk '{printf """$0"","}'

// 遍历每行,单引号(\047是单引号的转义)+逗号分隔+不换行输出
awk '{printf "\047%s\047,", $0}'

// 一行最多打印200个就换行
awk '{printf $0",";if(NR%200==0){printf "\n"}}'

// 指定逗号分隔符,给每列打上双引号+不换行+逗号分隔后输出
awk -F, '{for(i=1;i<=NF;i++){printf """$i"","}}'

其他文本处理工具学习(待探索)

  • grep

  • sed

总结

各种文本分析工具的功能都很强大、也比较复杂,一时半会儿不可能都掌握,需要多练习、有问题多学习。