我正在生成一个巨大的文件,其中包括如下部分:
~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~ 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:2
z ~
~ ~
f
1
和至少有 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
在职的
- 我们首先将范围隔离为线之间
/~ ~/
。外部的所有内容都会被删除。 - 范围异常值本身也会被删除。
- 现在我们有了要处理的正确行:在这些行中,我们将标记放置
\n
在第四个字段的开头,将另一个标记放置在第六个字段的末尾。 - 最后,我们删除这些标记之外的任何内容,剩下的是第四、第五和第六字段以及它们之间的空格(未修改)。
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
,但这会为某些数字添加额外的零。