使用 awk 读取特定行后的表格并统计出现次数

使用 awk 读取特定行后的表格并统计出现次数

我在一个大日志文件的某个地方有一张表,它如下例所示:

----------------------------
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))CH和...然后我得到有多少个CH

我已经并要创建一个像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.)然后将变量设置a1next意味着跳转到下一行并从该行重新开始处理。
  • a==1&&/NO LB/{b=1;next}检查是否a存在第二个字符串,以及是否在行中的某处找到1第二个字符串。它设置变量,然后加载行。NO LBbnext
  • $0==""{exit}:然后,如果该行为空,则退出处理(跳转到该END{}块)。
  • a==1&&b==1{c[$2]++}:如果找到两个匹配项(ab相等),则增加一个带有索引(字段 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 COORDINATEScount数组只计算第一个字段为数字的行。


如果您的文件与您显示的完全一样,其中连续的数据块由空行分隔,那么您可以使用 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

相关内容