按日期合并多个日志文件,其中还包括未注明日期的行(例如堆栈跟踪)

Evg*_*sky 7 linux unix merge command-line

如何合并日志文件,即按时间排序但也有多行的文件,其中只有第一行有时间,其余的没有。

日志1

01:02:03.6497,2224,0022 foo
foo1
2foo
foo3
01:04:03.6497,2224,0022 bar
1bar
bar2
3bar
Run Code Online (Sandbox Code Playgroud)

日志2

01:03:03.6497,2224,0022 FOO
FOO1
2FOO
FOO3
Run Code Online (Sandbox Code Playgroud)

预期结果

01:02:03.6497,2224,0022 foo
foo1
2foo
foo3
01:03:03.6497,2224,0022 FOO
FOO1
2FOO
FOO3
01:04:03.6497,2224,0022 bar
1bar
bar2
3bar
Run Code Online (Sandbox Code Playgroud)

如果不是以数字开头的非时间戳行,那么简单sort -nm log1 log2就行了。

在 unix/linux cmd 行上是否有一种简单的方法来完成工作?

编辑由于这些日志文件通常以 GB 为单位,因此合并应该在不重新排序(已经排序的)日志文件的情况下完成,也不要将文件完全加载到内存中。

ter*_*don 10

棘手。虽然可以使用date和 bash 数组,但这确实是一种可以从真正的编程语言中受益的东西。以 Perl 为例:

$ perl -ne '$d=$1 if /(.+?),/; $k{$d}.=$_; END{print $k{$_} for sort keys(%k);}' log*
01:02:03.6497,2224,0022 foo
foo1
2foo
foo3
01:03:03.6497,2224,0022 FOO
FOO1
2FOO
FOO3
01:04:03.6497,2224,0022 bar
1bar
bar2
3bar
Run Code Online (Sandbox Code Playgroud)

这是未压缩到注释脚本中的相同内容:

$ perl -ne '$d=$1 if /(.+?),/; $k{$d}.=$_; END{print $k{$_} for sort keys(%k);}' log*
01:02:03.6497,2224,0022 foo
foo1
2foo
foo3
01:03:03.6497,2224,0022 FOO
FOO1
2FOO
FOO3
01:04:03.6497,2224,0022 bar
1bar
bar2
3bar
Run Code Online (Sandbox Code Playgroud)

请注意,这假定所有日期行和日期行包含逗号。如果不是这种情况,您可以改用它:

perl -ne '$d=$1 if /^(\d+:\d+:\d+\.\d+),/; $k{$d}.=$_; END{print $k{$_} for sort keys(%k);}' log*
Run Code Online (Sandbox Code Playgroud)

上述方法需要将文件的全部内容保存在内存中。如果这是一个问题,这里有一个没有问题:

$ perl -pe 's/\n/\0/; s/^/\n/ if /^\d+:\d+:\d+\.\d+/' log* | 
    sort -n | perl -lne 's/\0/\n/g; printf'
01:02:03.6497,2224,0022 foo
foo1
2foo
foo3    
01:03:03.6497,2224,0022 FOO
FOO1
2FOO
FOO3    
01:04:03.6497,2224,0022 bar
1bar
bar2
3bar
Run Code Online (Sandbox Code Playgroud)

这只是通过替换换行符将连续时间戳之间的所有行放在一行中\0(如果这可以在您的日志文件中,请使用您知道永远不会出现的任何字符序列)。这传递给sort然后tr让线路回来。


正如 OP 非常正确地指出的那样,需要采用上述所有解决方案,并且不考虑可以合并文件。这是一个可以,但与其他的不同,它只能处理两个文件:

$ sort -m <(perl -pe 's/\n/\0/; s/^/\n/ if /^\d+:\d+:\d+\.\d+/' log1) \
            <(perl -pe 's/\n/\0/; s/^/\n/ if /^\d+:\d+:\d+\.\d+/' log2) | 
    perl -lne 's/[\0\r]/\n/g; printf'
Run Code Online (Sandbox Code Playgroud)

如果将 perl 命令保存为别名,则可以获得:

$ alias a="perl -pe 's/\n/\0/; s/^/\n/ if /^\d+:\d+:\d+\.\d+/'"
$ sort -m <(a log1) <(a log2) | perl -lne 's/[\0\r]/\n/g; printf'
Run Code Online (Sandbox Code Playgroud)