我有一个日志文件,我试图通过 bash 脚本“grep”数据。我特别关注的数据是两个时间戳之间的所有行(包括顶部时间戳),这些行具有模式“ERR-”,并且在每个条目之后包含一个空行以提高可读性。
示例日志文件:
Tue May 24 21:22:12 2022
ERR-0045 Lock detected in /tmp/file.lck
Tue May 24 21:44:12 2022
Errors in file /tmp/filename01.trc:
ERR-0001: Error detected. /tmp/filename.log
Tue May 24 21:47:25 2022
im some output
Tue May 24 21:47:25 2022
im some output too
im some output aswell
Tue May 24 21:48:03 2022
Errors in file /tmp/filename09.trc:
ERR-0100: error
ERR-0050: failure of sorts.
ERR-0052: line 3421
Tue May 24 21:49:07 2022
Completed process xyz
所以我想要的输出如下:
Tue May 24 21:22:12 2022
ERR-0045 Lock detected in /tmp/file.lck
Tue May 24 21:44:12 2022
Errors in file /tmp/filename01.trc:
ERR-0001: Error detected. /tmp/filename.log
Tue May 24 21:48:03 2022
Errors in file /tmp/filename09.trc:
ERR-0100: error
ERR-0050: failure of sorts.
ERR-0052: line 3421
我尝试过使用 sed/awk/cat 的组合,但没有取得太大成功。我遇到麻烦的地方是:
- ERR 之前并不总是包含时间戳的两行
- 一个时间戳块中可以有多个 ERR-
- 日期显然会改变,所以我不想硬编码,尽管格式不会改变。
提前致谢
答案1
脚本tst.awk
:
function print_r() { if (e) print r; r = ""; e = 0 }
/^([[:alpha:]]{3} ){2}[[:digit:]]{1,2} [[:digit:]]{2}(:[[:digit:]]{2}){2} [[:digit:]]{4}$/ {
print_r()
}
/^ERR-/{ e = 1 }
{ r = r ORS $0 }
END{ print_r() }
用法和输出:
$ awk -f tst.awk file
Tue May 24 21:22:12 2022
ERR-0045 Lock detected in /tmp/file.lck
Tue May 24 21:44:12 2022
Errors in file /tmp/filename01.trc:
ERR-0001: Error detected. /tmp/filename.log
Tue May 24 21:48:03 2022
Errors in file /tmp/filename09.trc:
ERR-0100: error
ERR-0050: failure of sorts.
ERR-0052: line 3421
您可以使用自己的表达式来表示时间戳或错误。这是所呈现格式的整行匹配(无日期验证)。并在行的开头添加“ERR-”。
笔记:
r
我们定义一个函数,如果发现错误 ( ),则打印记录 ( )e
。打印后还要重置这两个变量。对于awk
,尚未用值初始化的变量将被计算为空字符串或零。当日期的正则表达式匹配时,我们调用此函数。完成之前的日志记录并开始保存新的记录。因为记录是多行日志,所以我们还不知道是否必须打印它。
当错误模式匹配时,我们设置
e
。对于每一行,我们将行追加到现有记录中,并用
ORS
输出记录分隔符(默认换行符)分隔。此外,输出行之间的空行放置在 的开头r
,r
当我们在这里获取新时间戳时,始终是空字符串。我们
END
再次调用该函数,因为最后一条记录仍然保留着。
答案2
使用乐(以前称为 Perl_6)
~$ raku -e 'my @a=slurp; @a.=split(/ <?after ^^ ERR \V* > \n <?before ^^ Tue > /); \
put $_.subst(/^ <(.+)> ^^ Tue/) ~ "\n" if /^^ ERR / for @a;' file
问题似乎是您有一些带时间戳的记录有ERR
行,有些没有,您想丢弃后者。
上面,文件被slurp
编入@a
数组。 Then@a
位于以 开头的行结尾的记录之间split
,后跟零个或多个非垂直空白字符,以及以 开头的行之前。\n
<?after ^^ ERR \V* >
ERR
\V
<?before ^^ Tue >
Tue
在第二个声明中,output(…)
被精炼了。这些元素在该条款的序言中@a
单独(自动)表示。每个元素都经过条件测试,以确保它们包含以:开头的行,因此,该元素将被替换,删除前面的所有前导字符$_
for
if /^^ ERR/
ERR
if
$_
$_.subst(/^ <(.+)> ^^ Tue/)
内部的行首Tue
.本质上,这里.subst
没有替换意味着删除<(
…)>
捕获标记之间的匹配。为了分隔各个记录,$_.subst(…) ~ "\n"
元素通过~
波浪号与记录分隔"\n"
换行符连接起来。
输入示例:
Tue May 24 21:22:12 2022
ERR-0045 Lock detected in /tmp/file.lck
Tue May 24 21:44:12 2022
Errors in file /tmp/filename01.trc:
ERR-0001: Error detected. /tmp/filename.log
Tue May 24 21:47:25 2022
im some output
Tue May 24 21:47:25 2022
im some output too
im some output aswell
Tue May 24 21:48:03 2022
Errors in file /tmp/filename09.trc:
ERR-0100: error
ERR-0050: failure of sorts.
ERR-0052: line 3421
Tue May 24 21:49:07 2022
Completed process xyz
示例输出:
Tue May 24 21:22:12 2022
ERR-0045 Lock detected in /tmp/file.lck
Tue May 24 21:44:12 2022
Errors in file /tmp/filename01.trc:
ERR-0001: Error detected. /tmp/filename.log
Tue May 24 21:48:03 2022
Errors in file /tmp/filename09.trc:
ERR-0100: error
ERR-0050: failure of sorts.
ERR-0052: line 3421
当然,上面是一个简单的实现(它的目的是为了可读性)。要处理从一周中其他日子开始的日期,请将Tues
上面的内容替换为以下内容:
[ Mon || Tue || Wed || Thu || Fri || Sat || Sun ]
(如果您遇到特殊性问题,从上面的代码中应该可以清楚地看出如何向正则表达式添加月份)。