如何使sed匹配多行模式

时间:2017-09-14 11:13:27

标签: multithreading bash awk sed

背景

我使用sed来跟踪以与特定模式匹配的行开头的日志。我用这个命令:

sed -ne '/pattern/ p' infile >outfile

在代码中,我只是在前面添加带有标识符的日志行,以便我可以在以后使用它们进行过滤(我的标识符是订单号和线程号..所以例如这个日志行:

9/14/2017 10:19:58 AM:::   ORD7123::TH41361::Failed Checkout

用于订单7123线程41361.因此上面的sed命令(如果我对所有与订单7123相关的日志进行过滤)将如下所示:

sed -ne '/ORD7123/ p' infile >outfile

问题

当单个订单/线程组合的日志跨越多行时会出现问题:

9/14/2017 10:19:58 AM:::   ORD7123::TH41361::Failed Checking Out With Credit Card for # 3 times. Error: {
  "order": "country is required",
  "credit_card": "year is not a valid year"
}.
9/14/2017 10:19:59 AM:::   ORD7123::TH41347::Successfully Got a something Solution  

使用上面的sed命令,我的输出将如下所示

9/14/2017 10:19:58 AM:::   ORD7123::TH41361::Failed Checking Out With Credit Card for # 3 times. Error: {
9/14/2017 10:19:59 AM:::   ORD7123::TH41347::Successfully Got a something Solution

建议/分析

我们之前遇到过这个问题(我们控制了日志的创建),我们处理它的方式是用/n或类似的方式替换新行。在这种情况下,我无法控制日志创建,因此我必须按原样处理日志

4 个答案:

答案 0 :(得分:1)

awk应该能够处理 logstash 的工作方式并在多行上收集日志消息。看一下您的示例,您似乎想要在{ ... }之间捕获多行的文本。因此,您可以使用以下链接:

awk '/ORD7123/{if (/{$/) p=1; print; next} p; p && /^}/{p=0}' file.log

如果您不总是如示例日志中所示{ ... }那么您可以使用此awk命令:

awk '/ORD7123/ {p=NR} NR==p+1 {p = (/^[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{4} / ? 0 : NR)} p' file

9/14/2017 10:19:58 AM:::   ORD7123::TH41361::Failed Checking Out With Credit Card for # 3 times. Error: {
  "order": "country is required",
  "credit_card": "year is not a valid year"
}.
9/14/2017 10:19:59 AM:::   ORD7123::TH41347::Successfully Got a something Solution

awk命令在每个日志行中查找起始日期模式,如果找不到,则认为是以前日志消息的延续。

答案 1 :(得分:1)

awk 解决方案:

awk -v p="ORD7123" '$0~p{ print; 
           while(getline nl > 0 && (nl!~/^[0-9]+\/[0-9]{2}/ || nl~p)){ 
               print nl 
           } 
       }' inputfile

示例性输出:

9/14/2017 10:19:58 AM:::   ORD7123::TH41361::Failed Checking Out With Credit Card for # 3 times. Error: {
  "order": "country is required",
  "credit_card": "year is not a valid year"
}.
9/14/2017 10:19:59 AM:::   ORD7123::TH41347::Successfully Got a something Solution

答案 2 :(得分:0)

这可能适合你(GNU sed):

sed ':a;/ORD7123/!d;:b;n;/^..\?\/..\?\/.... ..:..:.. /ba;bb' file

匹配所需的字符串(ORD7123),否则删除该行。在匹配时,读取并打印后续行,直到以日期和时间开头的行,然后检查所需的字符串。

答案 3 :(得分:0)

sed用于单独行的简单替换,全部。你不是试图在一条线上做一个简单的替换,所以你不应该考虑sed。只需使用awk:

$ cat tst.awk
/^[0-9]/ { prt() }
{ rec = (rec=="" ? "" : rec ORS) $0 }
END { prt() }
function prt() {
    if ( rec ~ /ORD7123/) {
        print rec
    }
    rec = ""
}

$ awk -f tst.awk file
9/14/2017 10:19:58 AM:::   ORD7123::TH41361::Failed Checking Out With Credit Card for # 3 times. Error: {
  "order": "country is required",
  "credit_card": "year is not a valid year"
}.
9/14/2017 10:19:59 AM:::   ORD7123::TH41347::Successfully Got a something Solution

如果您希望通过将ORS更改为OFS(或您喜欢的任何其他字符串)来进一步处理,可以非常轻松地调整以删除记录中间的换行符)正在编制记录的地方:

$ cat tst.awk
/^[0-9]/ { prt() }
{ rec = (rec=="" ? "" : rec OFS) $0 }
END { prt() }
function prt() {
    if ( rec ~ /ORD7123/) {
        print rec
    }
    rec = ""
}

$ awk -f tst.awk file
9/14/2017 10:19:58 AM:::   ORD7123::TH41361::Failed Checking Out With Credit Card for # 3 times. Error: {   "order": "country is required",   "credit_card": "year is not a valid year" }.
9/14/2017 10:19:59 AM:::   ORD7123::TH41347::Successfully Got a something Solution