AWK:将行数附加到模式的下一个出现位置(在一个巨大的文件中)

AWK:将行数附加到模式的下一个出现位置(在一个巨大的文件中)

考虑这个文件:

#!/usr/bin/env bash
cat > example_file.txt <<EOL
TITLE something
some data
some data
some data
TITLE something else
some other data
TITLE some more
some other data
some other data
some other data
TITLE extra info
some more data
some more data
EOL

我需要添加一个新列:

  • 计算行数,
  • 发生后返回 1 /^TITLE/
  • 从文件底部开始向上移动,

基本上,结果应该是这样的:

TITLE something,4
some data,3
some data,2
some data,1
TITLE something else,2
some other data,1
TITLE some more,4
some other data,3
some other data,2
some other data,1
TITLE extra info,3
some more data,2
some more data,1

PS你可以假设:

  • 文件总是以匹配的行开始/^TITLE/
  • 文件总是以一行结尾不是匹配/^TITLE/
  • 没有两行连续匹配/^TITLE/

编辑:

目前结果

对于 100MB 的文件:

@Yarom

time tac trial.txt | awk 'BEGIN{x=0} {x++;{if ($1 !~/^pattern/) printf "%s,%s\n",$0,x;else if ($1 ~/^pattern/) {printf "%s,%s\n",$0,x;x=0}}}' | tac > trial2.txt
real    0m0,896s

@bac0n

 time awk '{ a[i++]=$0 } END { while (i--) { a[i]=a[i] "," ++j; if (a[i] ~ /^pattern/) { j=0 } }; for (i=0; i<NR; i++) { print a[i] } }' trial.txt > trial2.txt
real    0m0,830s

@oliv:

time awk -v RS='^pattern' -v FS='\n' '
{
  for(i=NF-1;i>0;i--) 
    printf "%s,%d\n",$i,i; 
    printf RT
}' trial.txt > trial2.txt

real    0m2,343s

@steeldriver

 time awk -vRS='\n(^pattern|$)' -F'\n' -vOFS=, '
   NR>1 {$1 = "^pattern" $1} 
   {for(i=1;i<=NF;i++) print $i, NF-i+1}
 ' trial.txt > trial2.txt
real    0m1,889s

使用 mawk 而不是 awk,我得到:

mawk: program limit exceeded: maximum number of fields size=32767

答案1

我设法编译了以下一行代码:

tac so_count.txt | awk 'BEGIN{x=0} {x++;{if ($1 != "TITLE") printf "%s,%s\n",$0,x;else if ($1 == "TITLE") {printf "%s,%s\n",$0,x;x=0}}}' | tac

我将进一步解释一下:

  • tac- 反转线的顺序(反向猫)。
  • awk- 如果第一列没有TITLE推进计数器,则TITLE打印当前计数并重置回 0。
  • tac- 将其反转。

结果:

TITLE something,4
DATA some data,3
DATA some data,2
DATA some data,1
TITLE something else,2
DATA some other data,1
TITLE some more,4
DATA some other data,3
DATA some other data,2
DATA some other data,1
TITLE extra info,3
DATA some more data,2
DATA some more data,1

祝你好运!

答案2

使用 awk:

awk -v RS='TITLE ' -v FS='\n' '
{
  for(i=NF-1;i>0;i--) 
    printf "%s,%d\n",$i,i; 
    printf RT
}' file

这依赖于设置的记录分隔符RS和字段分隔符FS来为计数器设置正确的起始值i

RT唯一的语句打印与之关联的每个字段以及计数器和记录终止符RS

该解决方案的优点是仅解析文件一次,不需要将整个文件放入内存中。

答案3

例子.awk

#!/bin/awk -f

{ a[i++]=$0 } END {
    while (i--) {
        a[i]=a[i] "," ++j
        if (a[i] ~ /^TITLE/) { j=0 }
    }
    for (i in a) { print a[i] }
}

例子

awk -f example.awk example.txt

输出

TITLE something,4
DATA some data,3
DATA some data,2
DATA some data,1
TITLE something else,2
DATA some other data,1
TITLE some more,4
DATA some other data,3
DATA some other data,2
DATA some other data,1
TITLE extra info,3
DATA some more data,2
DATA some more data,1

答案4

您可以将每个块视为一条记录,将每行视为一个字段 - 这样,您就可以减少每个块的计数,而无需反转文件或将多个块加载到内存中。

由于您的区块是由页眉而不是页脚划分的,因此需要一些技巧来处理第一条和最后一条记录。我能想到的最好的办法是:

awk -vRS='\n(TITLE|$)' -F'\n' -vOFS=, '
  NR>1 {$1 = "TITLE" $1} # replace the RS that got stripped off
  {for(i=1;i<=NF;i++) print $i, NF-i+1}
' example_file.txt

gawk这在和中都应该有效mawk。我怀疑它在低开销的 中运行速度会快得多mawkgawk如果你将语言环境设置为 C/POSIX,速度可能会相当。LC_ALL=C awk '...'

相关内容