AWK问题:打印N行,从给定/pattern/后的第三行开始

AWK问题:打印N行,从给定/pattern/后的第三行开始

我正在生成一个巨大的文件,其中包括如下部分:

~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~ Gradients ~~~~~~~~
~~~~~~~~~ x y z ~~~~~~~~~~
~ ~
~ H         1      0.00781      0.00108      0.00038 ~
~ H         2      0.01271     -0.01507      0.02839 ~
~ C         1     -0.05015     -0.01803      0.01588 ~
~ O         1      0.01733      0.03089     -0.04611 ~
~ O         2      0.01230      0.00114      0.00147 ~
~ ~
~~~~~~~~~~~~~~~~~~~~~~~~

我需要提取这些数字(xyz):

0.00781      0.00108      0.00038
0.01271     -0.01507      0.02839
-0.05015     -0.01803      0.01588
0.01733      0.03089     -0.04611 
0.01230      0.00114      0.00147

我写了以下脚本:

awk '/z ~/ {for(i=1; i<=6; i++) {getline; print $4, $5, $6}}' filename

但由于“~~”行,它给了我一个空行。

换句话说,每次找到/z ~/模式时,我想跳过另一行(模式+1),只打印其他五行(模式+2+3+4+5+6)的内容。当然,它需要重复操作(不断地进行,数十万次)。

答案1

awk解决方案:

awk '/z ~/{ n=NR+2 }n && n<=NR && NR<(n+5){ print $4,$5,$6 }' file | column -t

输出:

0.00781   0.00108   0.00038
0.01271   -0.01507  0.02839
-0.05015  -0.01803  0.01588
0.01733   0.03089   -0.04611
0.01230   0.00114   0.00147

  • NR- 当前记录数

  • n=NR+2-n这里指向模式行之后的“起始”行号

答案2

最简单的解决方案是添加另一个getline,然后得到 5 行而不是 6 行:

$ awk '/z ~/ {getline;for(i=1; i<=5; i++) {getline; print $4, $5, $6}}' file
0.00781 0.00108 0.00038
0.01271 -0.01507 0.02839
-0.05015 -0.01803 0.01588
0.01733 0.03089 -0.04611
0.01230 0.00114 0.00147

不过,就我个人而言,我会用稍微不同的方式来做:

$ awk '/z ~/{f=2;} /~ ~/{f--}; (f==1 && NF>5){print $4, $5, $6} ' file
0.00781 0.00108 0.00038
0.01271 -0.01507 0.02839
-0.05015 -0.01803 0.01588
0.01733 0.03089 -0.04611
0.01230 0.00114 0.00147

这里的想法是在匹配的行上设置一个标志(f变量),并在每次找到匹配的行时将其值减一。然后,我们仅在以下行上打印字段 4、5 和 6:2z ~~ ~f1 至少有 5 个字段。

对于这两个示例,为了获得漂亮的打印效果,您可以使用-vOFS="\t", 甚至更好printf

$ awk '/z ~/{f=2;} /~ ~/{f--}; (f==1 && NF>5){printf "%10s%10s%10s\n", $4, $5, $6} ' file
   0.00781   0.00108   0.00038
   0.01271  -0.01507   0.02839
  -0.05015  -0.01803   0.01588
   0.01733   0.03089  -0.04611
   0.01230   0.00114   0.00147

答案3

在职的

  1. 我们首先将范围隔离为线之间/~ ~/。外部的所有内容都会被删除。
  2. 范围异常值本身也会被删除。
  3. 现在我们有了要处理的正确行:在这些行中,我们将标记放置\n在第四个字段的开头,将另一个标记放置在第六个字段的末尾。
  4. 最后,我们删除这些标记之外的任何内容,剩下的是第四、第五和第六字段以及它们之间的空格(未修改)。

sed -ne '
   /~ ~/,//!d
   //d
   s/[^[:space:]]\{1,\}/&\n/6
   s/[^[:space:]]\{1,\}/\n&/4
   s/.*\n\(.*\)\n.*/\1/p
' yourfile

结果

0.00781      0.00108      0.00038
0.01271     -0.01507      0.02839
-0.05015     -0.01803      0.01588
0.01733      0.03089     -0.04611
0.01230      0.00114      0.00147

答案4

就匹配这些行而言,/^~ [A-Z]/模式就足够了,并且对于每个打印,对应的字段 4,5 和 6。

awk 版本是:

$ awk '/^~ [A-Z]/{printf("%-8s\t%-8s\t%-8s\n",$4,$5,$6)}' input.txt
0.00781     0.00108     0.00038 
0.01271     -0.01507    0.02839 
-0.05015    -0.01803    0.01588 
0.01733     0.03089     -0.04611
0.01230     0.00114     0.00147 

以及同样的事情的 perl 翻译:

$ perl -ane 'printf("%-8s\t%-8s\t%-8s\n",$F[3],$F[4],$F[5]) if /^~ [A-Z]/' input.txt                                     
0.00781     0.00108     0.00038 
0.01271     -0.01507    0.02839 
-0.05015    -0.01803    0.01588 
0.01733     0.03089     -0.04611
0.01230     0.00114     0.00147 

请注意,这里我们使用printf()带有左对齐标志的函数%-8s来进行正确的格式化。

另一种方法是将所需的数字视为浮点数字,并使用%f说明符而不是%-8s,但这会为某些数字添加额外的零。

相关内容