Awk getline需要更多的时间

时间:2017-11-06 21:24:44

标签: awk

有一个(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下。

2 个答案:

答案 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"