web-dev-qa-db-fra.com

Lire le tableau après une ligne spécifique et compter les occurrences avec awk

J'ai une table quelque part dans un gros fichier journal qui ressemble à cet exemple:

----------------------------
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)
--------------------------------

Je souhaite indiquer à awk de rechercher la CARTESIAN COORDINATES (A.U.), puis à trouver NO LB, puis à lire la deuxième variable de chaque ligne jusqu'à atteindre l'espace vide précédant -----.

Donc, je vais lire tous les (éléments Carbone (C) Oxygène (O) Hydrogène (H)) C 'H et ... alors je obtenez combien de C 'H.

J'ai et je peux créer une variable telle que C5H2 dans ce cas, il peut s'agir de quelque chose comme C3OH4, des idées?

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


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

Utilisez cette 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}: Ce bloc recherche CARTESIAN COORDINATES (A.U.) puis définit la variable a sur 1, next signifie passer à la ligne suivante et recommencer le traitement avec cette ligne.
  • a==1&&/NO LB/{b=1;next} vérifie si a est 1 et si la deuxième chaîne NO LB se trouve quelque part dans la ligne. Il définit la variable b, puis charge la ligne next.
  • $0==""{exit}: Ensuite, si la ligne est vide, quitte le traitement (elle passe au bloc END{}).
  • a==1&&b==1{c[$2]++}: Si les deux correspondances sont trouvées (a et b égal à 1) incrémenter un tableau appelé c avec l'indice $2 (champ 2) . Cela comptera les occurrences de chaque valeur dans le deuxième champ.
  • END{...}: Ceci s'exécutera une fois le traitement du fichier terminé (le tableau est rempli).
    • for(i in c) passe par chaque élément du tableau ...
    • printf "%s%s", i,c[i]: ... et imprimer l'index et la valeur.

La sortie (avec votre fichier d'exemple):

C5H2
6
chaos

Encore une autre version de 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 

C'est en quelque sorte un mélange entre la réponse de Serg et celle de Chaos. Il ne fonctionnera qu'entre les lignes correspondant à NO.*[[:blank:]]LB et INTERNAL COORDINATES. Le tableau count ne compte que sur les lignes dont le premier champ est un nombre.


Si votre fichier est exactement tel que vous le montrez, où des blocs de données successifs sont séparés par une ligne vide, vous pouvez utiliser le "mode paragraphe" de Perl, qui traite les paragraphes comme des lignes:

Perl -00ne 'next unless /CARTESIAN COORDINATES \(A\.U\.\)/; 
            $count{$_}++ for (/\s+\d+\s+(\w+)\s/g); 
            print "$_$count{$_}" for keys(%count)' file 

Explication

  • -00: activer le mode paragraphe;
  • next unless /CARTESIAN COORDINATES \(A\.U\.\)/; ignore ce paragraphe s'il ne correspond pas à CARTESIAN COORDINATES (A.U.);
  • $count{$_}++ for (/\n\s+\d+\s+(\w+)\s/g): l'expression régulière recherche un ou plusieurs caractères d'espacement (\s+), suivis d'un ou plusieurs chiffres (\d+), un ou plusieurs caractères d'espacement, puis un ou plusieurs caractères Word (\w+) suivi d'un caractère d'espacement. Cela devrait identifier tous les éléments. %count est un hachage, un tableau associatif. Il a des clés et chaque clé est associée à une valeur. Le $count{$_}++ for ... enregistre chacune des correspondances de l'expression rationnelle ci-dessus sous forme de clé dans ce hachage et incrémente sa valeur d'une unité à chaque fois qu'elle est trouvée. Le résultat est un hachage qui stocke les éléments et le nombre de fois où chacun a été trouvé.
  • print "$_$count{$_}" for keys(%count): pour chacun des éléments (les clés du hachage %count), affiche l'élément et le nombre de fois où il a été trouvé.

Exécutez sur votre exemple de fichier, cela retourne:

$ Perl -00ne 'next unless /CARTESIAN COORDINATES \(A\.U\.\)/; 
            $count{$_}++ for (/\s+\d+\s+(\w+)\s/g); 
            print "$_$count{$_}" for keys(%count)' file 
C5H2$

Cependant, il manque la nouvelle ligne finale, vous pouvez donc l'ajouter avec:

$ 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

la réponse du chaos fonctionne très bien pour accomplir ce que vous voulez. Voici une alternative plus simple au cas où,

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

Donne le résultat C5H2.

2
H. Freeze

Voici un code un peu plus simple:

awk '/NO.*[[:blank:]]LB/,/INTERNAL COORDINATES/ { if ( $2 == "C")  counterC++; if ($2 == "H") counterH++  } END {print "C"counterC"H"counterH} ' coordinates.txt

Exemple de sortie:

$ awk '/NO.*[[:blank:]]LB/,/INTERNAL COORDINATES/ { if ( $2 == "C")  counterC++; if ($2 == "H") counterH++  } END {print "C"c>
C5H2
2
Sergiy Kolodyazhnyy