我在一个大日志文件的某个地方有一张表,它如下例所示:
----------------------------
CARTESIAN COORDINATES (A.U.)
----------------------------
NO LB ZA FRAG MASS X Y Z
0 C 6.0000 0 12.011 -8.817666638854597 -4.911814574090662 58.264165798697491
1 C 6.0000 0 12.011 -7.879568488830738 -4.388761616508626 55.950914108733443
2 C 6.0000 0 12.011 -7.790669273242299 -4.339145245237274 60.527363919786708
3 C 6.0000 0 12.011 -7.070247938157430 -3.937287748509576 62.694740665963295
4 C 6.0000 0 12.011 -7.244178391763230 -4.034368638160922 53.748929835486599
5 H 1.0000 0 1.008 -6.427462410780078 -3.581016558829315 64.562423911622218
6 H 1.0000 0 1.008 -6.674286700050606 -3.718319003596096 51.850593400164620
--------------------------------
INTERNAL COORDINATES (ANGSTROEM)
--------------------------------
我想告诉awk
找到CARTESIAN COORDINATES (A.U.)
然后找到NO LB
然后开始读取每一行中的第二个变量直到到达之前的空白处-----
。
因此,我将读取所有(元素碳(C
)氧(O
)氢(H
))C
的H
和...然后我得到有多少个C
的H
。
我已经并要创建一个像C5H2
这种情况的变量,它最终可能会是任何类似的东西C3OH4
,有什么想法吗?
awk '
/CARTESIAN COORDINATES (A.U.)/ {fcart=1}
fcart &&
/ NO LB/ {scart=1}
/---------------------------/{exit}
' OFS="\t" "$FILENAME"
答案1
用这个awk
:
awk '/CARTESIAN COORDINATES \(A.U.\)/{a=1;next} a==1&&/NO LB/{b=1;next} $0==""{exit}
a==1&&b==1{c[$2]++} END{for(i in c){printf "%s%s", i,c[i]}}' file
/CARTESIAN COORDINATES \(A.U.\)/{a=1;next}
:此块搜索CARTESIAN COORDINATES (A.U.)
然后将变量设置a
为1
,next
意味着跳转到下一行并从该行重新开始处理。a==1&&/NO LB/{b=1;next}
检查是否a
存在第二个字符串,以及是否在行中的某处找到1
第二个字符串。它设置变量,然后加载行。NO LB
b
next
$0==""{exit}
:然后,如果该行为空,则退出处理(跳转到该END{}
块)。a==1&&b==1{c[$2]++}
:如果找到两个匹配项(a
且b
相等),则增加一个带有索引(字段 2)1
的数组。这将计算第二个字段中每个值的出现次数。c
$2
END{...}
:这将在文件处理完成(数组已填充)时运行。for(i in c)
遍历数组中的每个元素......printf "%s%s", i,c[i]
:...并打印索引和值。
输出(包含您的示例文件):
C5H2
答案2
还有另一个 awk 版本:
awk '/NO.*[[:blank:]]LB/,/INTERNAL COORDINATES/ {
if($1~/[0-9]/){count[$2]++;}}
END {for(i in count){printf "%s%s",i,count[i]}print ""} ' file
这有点像 Serg 的答案和 Chaos 的答案的混合。它只会在匹配NO.*[[:blank:]]LB
和 的行之间运行INTERNAL COORDINATES
。count
数组只计算第一个字段为数字的行。
如果您的文件与您显示的完全一样,其中连续的数据块由空行分隔,那么您可以使用 Perl 的“段落模式”,它将段落视为行:
perl -00ne 'next unless /CARTESIAN COORDINATES \(A\.U\.\)/;
$count{$_}++ for (/\s+\d+\s+(\w+)\s/g);
print "$_$count{$_}" for keys(%count)' file
解释
-00
:开启段落模式;next unless /CARTESIAN COORDINATES \(A\.U\.\)/;
如果不匹配,则跳过此段落CARTESIAN COORDINATES (A.U.)
;$count{$_}++ for (/\n\s+\d+\s+(\w+)\s/g)
:正则表达式查找一个或多个空格字符(\s+
),后跟一个或多个数字(\d+
),然后是一个或多个空格字符,然后\w+
是一个或多个单词字符( ),后跟一个空格字符。这应该可以识别所有元素。%count
是一个哈希,一个关联数组。它有键,每个键都与一个值相关联。$count{$_}++ for ...
将把上述正则表达式的每个匹配项保存为该哈希中的键,并在每次找到时将其值加一。结果是一个哈希,其中存储了元素以及找到每个元素的次数。print "$_$count{$_}" for keys(%count)
:对于每个元素(哈希的键%count
),打印该元素及其被发现的次数。
运行示例文件,将返回:
$ perl -00ne 'next unless /CARTESIAN COORDINATES \(A\.U\.\)/;
$count{$_}++ for (/\s+\d+\s+(\w+)\s/g);
print "$_$count{$_}" for keys(%count)' file
C5H2$
但是,缺少最后一个换行符,因此您可以使用以下命令添加它:
$ perl -00ne 'next unless /CARTESIAN COORDINATES \(A\.U\.\)/;
$count{$_}++ for (/\s+\d+\s+(\w+)\s/g);
print "$_$count{$_}" for keys(%count); print "\n"' file
C5H2
答案3
这是一个稍微简单一点的代码:
awk '/NO.*[[:blank:]]LB/,/INTERNAL COORDINATES/ { if ( $2 == "C") counterC++; if ($2 == "H") counterH++ } END {print "C"counterC"H"counterH} ' coordinates.txt
示例输出:
$ awk '/NO.*[[:blank:]]LB/,/INTERNAL COORDINATES/ { if ( $2 == "C") counterC++; if ($2 == "H") counterH++ } END {print "C"c>
C5H2
答案4
chaos 的答案非常有效,可以实现你想要的。这里有一个更简单的替代方案,以防万一,
awk 'BEGIN{}
$2 ~ /^C$/ { countC++; } $2 ~ /^H$/ { countH++ }
END { print "C",countC,"H",countH; }' OFS="" file
给出输出C5H2
。