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