我有一个大文件,其日志如下所示。大约有 30000 个此类事件被记录。我需要提取以RINGING
and CLOSE
(包含)开头并且不包含 的那些行30 30
。
要求是:
在下面看到的两个实例中,我只需要保留实例 2。实例 1 需要完全删除(它们构成了文件的大部分)
实例1:
313782 Aug 19 18:37:04.925: <DATA> RINGING|254|01136097645|5950|$hostIp|$size |$data
313783 Aug 19 18:37:05.262: <DATA> TRAINING|254|01136097645|5950|$hostIp|$size |$data
313784 Aug 19 18:37:09.028: <DATA> OUT |254|01136097645|5950|$hostIp|2 bytes |30 93
313785 Aug 19 18:37:09.705: <DATA> IN |254|01136097645|5950|$hostIp|4 bytes |30 73 F9 F8
313786 Aug 19 18:37:18.532: <DATA> IN |254|01136097645|5950|$hostIp|336 bytes |30 10 60 00 06 00 00 6F 12 00 ...
313787 Aug 19 18:37:19.485: <DATA> OUT |254|01136097645|5950|$hostIp|133 bytes |30 30 60 00 00 00 06 6F 12 10 ...
313788 Aug 19 18:37:20.898: <DATA> TRAINING|254|01136097645|5950|$hostIp|$size |$data
313789 Aug 19 18:37:22.006: <DATA> CLOSE|254|01136097645|5950|$hostIp|$size |$data
实例2:
(带有 30 30 的行不存在)
313782 Aug 19 18:37:04.925: <DATA> RINGING|254|01136097645|5950|$hostIp|$size |$data
313783 Aug 19 18:37:05.262: <DATA> TRAINING|254|01136097645|5950|$hostIp|$size |$data
313784 Aug 19 18:37:09.028: <DATA> OUT |254|01136097645|5950|$hostIp|2 bytes |30 93
313785 Aug 19 18:37:09.705: <DATA> IN |254|01136097645|5950|$hostIp|4 bytes |30 73 F9 F8
313786 Aug 19 18:37:18.532: <DATA> IN |254|01136097645|5950|$hostIp|336 bytes |30 10 60 00 06 00 00 6F 12 00 ...
313788 Aug 19 18:37:20.898: <DATA> TRAINING|254|01136097645|5950|$hostIp|$size |$data
313789 Aug 19 18:37:22.006: <DATA> CLOSE|254|01136097645|5950|$hostIp|$size |$data
答案1
假设您的日志文件名为logfile
,以下是awk
带有示例输出的解决方案:
$ awk '/RINGING/,/CLOSE/ {if (/30 30/){f=1}; a=a"\n"$0} f==0 && /CLOSE/ {print a} /CLOSE/{a="";f=0}' logfile
313782 Aug 19 18:37:04.925: <DATA> RINGING|254|01136097645|5950|$hostIp|$size |$data
313783 Aug 19 18:37:05.262: <DATA> TRAINING|254|01136097645|5950|$hostIp|$size |$data
313784 Aug 19 18:37:09.028: <DATA> OUT |254|01136097645|5950|$hostIp|2 bytes |30 93
313785 Aug 19 18:37:09.705: <DATA> IN |254|01136097645|5950|$hostIp|4 bytes |30 73 F9 F8
313786 Aug 19 18:37:18.532: <DATA> IN |254|01136097645|5950|$hostIp|336 bytes |30 10 60 00 06 00 00 6F 12 00
313788 Aug 19 18:37:20.898: <DATA> TRAINING|254|01136097645|5950|$hostIp|$size |$data
313789 Aug 19 18:37:22.006: <DATA> CLOSE|254|01136097645|5950|$hostIp|$size |$data
解释
awk
依次执行每个命令:
/RINGING/,/CLOSE/ {if (/30 30/){f=1}; a=a"\n"$0}
表达式
/RINGING/,/CLOSE/
是一个范围:它指定该命令仅适用于行组。当遇到包含文本的行时,组开始RINGING
。当CLOSE
遇到包含文本的行时,该组结束。对于此类组中的任何行,都会执行大括号中的命令。f
如果该行包含 ,则第一个将标志设置为 130 30
。第二个命令将当前行追加到变量中a
。f==0 && /CLOSE/ {print a}
这里大括号中的命令前面有两个条件并连在一起。第一个条件指定标志
f
为零(意味着30 30
在该组中未找到),第二个条件指定该行包含文本CLOSE
。如果满足这两个条件,则a
打印存储在变量中的行组。/CLOSE/{a="";f=0}
最后,在包含 text 的任何行上
CLOSE
,变量a
将重置为空字符串,并将标志f
设置为零。完成此操作后,代码就准备好在下一组行(如果有)上开始。
答案2
例如,可以使用 perl 兼容的正则表达式 (PCRE)
pcregrep -M '^.*?RINGING(?(?!30 30)(?s).)+?CLOSE.*?$' file
或者
grep -zPo '^.*?RINGING(?(?!30 30)(?s).)+?CLOSE.*?$' file
或者,使用 GNU awk 更具表现力的记录分隔符
gawk -vRS="CLOSE[^\n]*\n" -vORS= '!/30 30/ {print; print RT}' file