Comment replace toutes les premières occurrences dans les lignes appariées avec un nombre incrémentiel?

J'ai un file comme celui-ci

... 1562 first part 1563 H col3 H col4 1564 H col3 H col4 ... 3241 H col3 H col4 3242 third part ... 

Je veux replace seulement le premier H de chaque ligne par H# , avec # est son nombre d'apparition. La sortie devrait être:

 ... 1562 first part 1563 H1 col3 H col4 1564 H2 col3 H col4 ... 3241 H1652 col3 H col4 3242 third part ... 

Jusqu'à présent, j'ai essayé:

 max=`grep -c ' H ' b` while [[ "$i" -le $max ]];do grep -m $i ' H ' b|tail -n1|sed "s/H/H$i/1" let i=i+1 done 

Ce code est lent, il doit lire chaque ligne à replace et ne peut pas append la première partie et la troisième partie du file. Y a-t-il une meilleure façon de le faire? Peut-être awk? Je vous remercie.

Vous pouvez par exemple utiliser ceci:

 $ awk '/H/{sub("H", "H"++v)}1' file 1562 first part 1563 H1 col3 H col4 1564 H2 col3 H col4 3241 H3 col3 H col4 3242 third part ... 

Cela search les lignes contenant H et remplace H par H avec une variable que nous continuons à incrémenter. Notez que vous pourriez utiliser gsub() au lieu de sub() si vous vouliez effectuer cette modification dans tous les templates correspondants à la place d'un seul.

Le dernier 1 est une condition vraie, donc il exécute l'action awk par défaut: {print $0} , c'est-à-dire imprime la ligne complète.

Essaye ça:

  awk 'BEGIN { hNum = 1; } { if ($2 == "H") { $2 = "H" hNum; hNum++; } print $0; }' yourFile > outFile 

Il exécute awk utilisant l'espace comme séparateur, donc $2 est le deuxième jeton de chaque ligne et si $2 est égal à "H", remplacez-le par "H" suivi du numéro commençant par 1. Enfin, imprimez la ligne.

Avec perl :

 perl -pe 's/\bH\b\K/++$i/e' file 

Vous pouvez replace -pe par -pi.back -e pour l'édition sur place avec l'original enregistré en tant que file.back ou -pi -e pour aucune sauvegarde.

 { nl -bpH -w1 | sed 's/^\([0-9]*\)[ \t]*\([^H]*.\)/\2\1/' } <<\DATA ... 1562 first part 1563 H col3 H col4 1564 H col3 H col4 ... 3241 H col3 H col4 3242 third part DATA 

SORTIE

 ... 1562 first part 1563 H1 col3 H col4 1564 H2 col3 H col4 ... 3241 H3 col3 H col4 3242 third part 

C'est le moyen le plus rapide que je puisse imaginer, surtout avec un très gros file. nl ne numérotera que les lignes contenant la string H et inserta ce numéro en tête de ligne suivi d'un caractère <tab> . Il indente toutes les autres lignes avec quelques espaces.

sed est passé la sortie de nl sur la | tuyau. sed puis remplace la séquence suivante:

  • 0 ou plusieurs numbers apparaissant au début de la ligne (référencés \1 )
  • 0 ou plus <tab> ou <space> caractères
  • 0 caractères ou plus qui ne sont pas H, puis un caractère (référencé par \2 )

… avec \2\1 .

Donc, les lignes ne contenant pas de H obtiennent ce traitement:

 ^'' .*.$ = ^.*.''$ 

Et ceux qui obtiennent celui-ci:

 ^(digit)*<tab>(not H)*H.*$ = ^(not H)*H(digit)*.*$ 

… où '' est une string vide.

Pour une portabilité maximale, remplacez le \t dans [ \t] par un caractère littéral <tab> .