web-dev-qa-db-ja.com

特定の行の後の表を読み取り、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)
--------------------------------
_

awkCARTESIAN COORDINATES (A.U.)を見つけ、_NO LB_を見つけてから、_-----_の前の空白に達するまで各行の2番目の変数の読み取りを開始するように指示したい。

したがって、すべての要素(炭素(C)酸素(O)水素(H))CHと...次に、CHの数を取得します。

この場合、_C5H2_のような変数を作成しますが、最終的には_C3OH4_のようなものになるかもしれません。

_awk '
/CARTESIAN COORDINATES (A.U.)/ {fcart=1}
fcart &&
/  NO LB/ {scart=1}


/---------------------------/{exit}
' OFS="\t" "$FILENAME"
_

この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_であるかどうか、および2番目の文字列_NO LB_が行のどこかにあるかどうかを確認します。変数bを設定し、next行をロードします。
  • _$0==""{exit}_:行が空の場合、処理を終了します(_END{}_ブロックにジャンプします)。
  • _a==1&&b==1{c[$2]++}_:両方の一致が見つかった場合(aおよびb equal _1_)cという配列をインデックス_$2_(フィールド2)。これにより、2番目のフィールドの各値の出現回数がカウントされます。
  • _END{...}_:これは、ファイル処理が完了すると実行されます(配列がいっぱいになります)。
    • for(i in c)配列内の各要素を介して実行...
    • _printf "%s%s", i,c[i]_:...インデックスと値を出力します。

出力(サンプルファイルを使用):

_C5H2
_
6
chaos

さらに別の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):正規表現は、1つ以上の空白文字(_\s+_)に続いて1つ以上の数字(_\d+_)、1つ以上の空白文字、さらに1つ以上を探しますワード文字(_\w+_)の後に空白文字が続きます。これにより、すべての要素が識別されます。 _%count_はハッシュ、連想配列です。キーがあり、各キーは値に関連付けられています。 _$count{$_}++ for ..._は、上記の正規表現の一致のそれぞれをそのハッシュのキーとして保存し、検出されるたびに値を1ずつ増やします。結果は、要素とそれぞれが見つかった回数を格納するハッシュです。
  • 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
_
4
terdon

カオスの答えは、あなたが望むものを達成するために非常にうまく機能します。念のため、より簡単な代替手段を示します。

awk 'BEGIN{}
$2 ~ /^C$/ { countC++; } $2 ~ /^H$/ { countH++ }
END { print "C",countC,"H",countH; }' OFS="" file

出力C5H2

2
H. Freeze

これはやや単純なコードです。

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
2