从给定范围内的数据块以及每个块中的给定行范围内提取文件行

从给定范围内的数据块以及每个块中的给定行范围内提取文件行

我有一个这种形式的数据文件

1 4
2 0
2 3
3 5
5 3
8 12
2 3
3 5
5 3
0 -1
2 4
33 3

该文件有 12 行,应解释为四个连续的数据块,每块 3 行。例如,第三个块是

2 3
3 5
5 3

如何在每个块中从 m 到 n 的块中提取从 i 到 j 的行,并将它们输出到 Linux 中的文件中?

例如,对于i=2, j=3, m=1, n=3,所需的结果应如下所示

2 0
2 3
5 3
8 12
3 5
5 3 

谢谢。

答案1

以下awk程序应该执行以下操作:

awk -v bs=3 -v i=2 -v j=3 -v m=1 -v n=3 '(FNR/bs>m-1) && (FNR/bs<=n) && ((FNR-1)%bs>=i-1) && ((FNR-1)%bs<j)' input.txt 

这会将您的关键数据作为awk变量导入到程序中:

  • 块大小作为变量bs
  • 开始和结束块号作为变量mn
  • 开始和结束行号作为变量ij

它使用这样awk的逻辑:“规则”块之外的任何条件计算结果为true(或非零)都指示awk打印当前行。

打印基于自动变量FNR,代表每个文件的行计数器。您的要求基本上相当于通过将数字FNR除以块大小来识别块编号,并通过计算 的模数FNR(为方便起见,我们使用FNR-1从 0 开始)来识别块内的行,然后制定一个布尔表达式,该表达式仅true对于您想要打印的那些行。

更新

可以通过尽可能避免耗时的操作来“加速”程序。为此,您可以按如下方式修改程序:

awk ... 'BEGIN{first=bs*(m-1)+1; last=bs*n}
         FNR<first{next}
         FNR>last{exit}
         ((FNR-1)%bs>=i-1) && ((FNR-1)%bs<j)' input.txt

这将首先确定要考虑的第一行和最后一行。

  • 如果当前行号在第一个块的开头之前,我们立即跳到下一行,并且不执行任何计算和比较来“细粒度”检查是否打印。
  • 同样,如果当前行号超过了要考虑的最后一个块,我们立即终止程序。
  • 仅当我们位于“感兴趣区域”内时,才会执行算术运算来检查要打印的行。

这样,我们就可以将计算量保持在绝对最低限度。

如果您使用的是 GNU 变体awk,并且已指定多个输入文件作为参数,请使用nextfile而不是exit跳到下一个文件而不是退出程序。

答案2

使用 GNU sed 和 awk 的替代解决方案:

# Split data into data-blocks
<infile sed '3~3G'                              |

# Only pass blocks m through n onwards
awk 'NR >= m && NR <= n' RS= ORS='\n\n' m=1 n=3 |

# Only print lines i through j within each block
awk '{ for (x=i ; x<=j; x++) print $x }' RS= FS='\n' i=2 j=3

答案3

使用 GNU sed 和单独的流选项 (-s) 将命令行中的多个文件作为单独的文件进行处理。

i=2 j=3 m=1 n=3 G=3
split -l "$G" file
printf '%s\n' x?* |
sed -e "$m,$n!d;${n}q" |
xargs sed -s "$i,$j!d"

答案4

使用 Raku(以前称为 Perl_6)

raku -e '.join("\n").put for lines.rotor(3)[0..2].map(*.[1..2]);'  

输入示例:

1 4
2 0
2 3
3 5
5 3
8 12
2 3
3 5
5 3
0 -1
2 4
33 3

示例输出:

2 0
2 3
5 3
8 12
3 5
5 3

上面是用 Raku(Perl 编程语言家族的成员)编写的解决方案。简而言之,lines被读入(懒惰地)和rotor-ed (即分组)在一起,3每个行都精确分组(末尾不完整的组将随上面的代码一起删除,如果您希望返回部分组,请添加正确的“副词”选项,像这样rotor(3, :partial):)。

前 3 组每三行被保留(使用[0..2]索引构造),并map(*.[1..2])在这三组内执行映射以仅返回第二个和第三个元素(Perl 系列语言如 Raku 从零开始索引)。

上面的代码返回所需的结果,但是如果程序员愿意,结果可以按行/引用返回,如下所示:

raku -e '.raku.put for lines.rotor(3)[0..2].map(*.[1..2]);'  file
("2 0", "2 3")
("5 3", "8 12")
("3 5", "5 3")

https://raku.org

相关内容