有一个(G | M)awk脚本,用于根据内容将源文件中的行重新分配到不同的文件中。一个示例是基于行时间戳将一组源数据分片为单独的日期文件。
它足够快,直到我需要添加一个csv标题行。源数据是无序的,因此我考虑使用if (getline < (fname) < 0) { print cols > fname }
在第一次触摸时使用标题行创建每个目标文件。
我知道对源数据的每一行进行此测试可能会很昂贵,但似乎在创建目标文件时,每次getline
测试都会花费更长的时间,因为处理的源文件的速度会变慢。减速是性能问题。
此过程也在GNU Parallel下运行,因此system -f
测试不能挂在系统调用上。
有关如何在使用标题创建文件时解决性能问题的建议?希望将此脚本保存在Awk中,因为已经存在其他逻辑。
作为该任务的一个示例,我有来自多个主机的日志,这些主机需要根据日志条目的日期组合成文件:
date, time, measure
2017-01-01, 00:00, 10
2017-01-01, 01:00, 20
2017-01-03, 00:05, 30
2017-01-02, 02:10, 40
2017-01-03, 00:00, 50
此脚本的结果将是基于日期列的3个文件:
文件名:20170101.log
date, time, measure
2017-01-01, 00:00, 10
2017-01-01, 01:00, 20
文件名:20170102.log
date, time, measure
2017-01-02, 02:10, 40
文件名:20170103.log
date, time, measure
2017-01-03, 00:05, 30
2017-01-03, 00:00, 50
在需要包含列标题之前,运行此日志重组非常简单快捷。似乎随着目标文件的增长,getline
操作每次调用都需要更长的时间。其他示例显示了使用system
调用test -f
来存在文件,但这也是一个昂贵的操作,似乎挂在GNU Parallel下。
答案 0 :(得分:1)
我认为我采用的方法是维护输出文件/管道数组,因为它们会显示输入,并根据数组成员资格而不是输出文件存在来添加标头。测试数组成员资格应该超级快,至少与在shell中生成test
相比。
这样的事情:
BEGIN {
getline header # this assumes we'll see a header as our
} # first line of input. Use whatever works.
{
outfile=$1 ".log" # or whatever..
if (!(outfile in a)) {
print header > outfile # create the file with the header,
a[outfile] # and record the output file.
}
print > outfile # shard
}
这消除了触摸文件系统以测试存在的需要,但如果您有要附加的现有文件,则可能会出现问题。为此,您可能希望在BEGIN
块中预填充数组:
BEGIN {
getline header
cmd="ls -d *.log 2>/dev/null"
while (cmd | getline outfile) a[outfile]
close(cmd)
}
{
outfile=$1 ".log"
if (!(outfile in a)) {
print header > outfile
a[outfile]
}
print >> outfile
}
注意:此变体解析ls
(ya ya,我知道)获取预填充数组的文件列表,然后附加数据(>>
)而不是覆盖({{1} })。我还没有在可能包含特殊字符的日志文件上对此进行测试。另一方面,文件名是>
,所以特殊性已经有限了。
答案 1 :(得分:0)
事实证明我的问题是记录分隔符的问题。
源文件使用RS =“\ r \ n”(在BEGIN块中设置),但目标文件使用“\ n”作为分隔符,因为它们只是打印而且在Linux上。这导致getline看不到行,所以当目标文件增长时,getline的时间也是如此 - 它正在读取整个文件。
我相当不优雅的解决方案是:
RS="\n"
if (getline < (fname) < 0) { print cols > fname }
RS="\r\n"