script awk pour remplacer plusieurs occurrences de modèle de chaîne dans la même ligne dans différents fichiers par un numéro correspondant à la chaîne

Nov 26 2020

J'ai besoin d'un script awk qui recherche une chaîne à l'intérieur de <>, s'il en trouve une qu'il n'a pas trouvée avant, il doit le remplacer par la valeur actuelle du compteur d'index (0 au début) et incrémenter le compteur. S'il trouve une chaîne à l'intérieur de <> qu'il connaît déjà, il doit rechercher l'index de la chaîne et le remplacer par l'index. Cela doit être fait sur plusieurs fichiers, ce qui signifie que le compteur ne se réinitialise pas lorsque plusieurs fichiers sont recherchés pour les modèles, uniquement au démarrage du programme Par exemple: file_a.txt:

123abc<abc>xyz
efg
<b>ah
a<c>, <abc>
<c>b
(<abc>, <b>)

file_b.txt:

xyz(<c>, <b>)
xyz<b>xy<abc>z

devrait devenir

file_a_new.txt:

123abc<0>xyz
efg
<1>ah
a<2>, <0>
<2>b
(<0>, <1>)

file_b_new.txt:

xyz(<2>, <1>)
xyz<1>xy<0>z

Ce que j'ai jusqu'à présent:

awk 'match($0, /<[^>]+>/) { k = substr($0, RSTART, RLENGTH)
   if (!(k in freq))
      freq[k] = n++
   $0 = substr($0, 1, RSTART-1) freq[k] substr($0, RSTART+RLENGTH) } { print $0 > (FILENAME ".tmp")
}' files

Mais cela ne peut détecter qu'un seul motif <> par ligne, mais il peut y avoir plusieurs motifs <> par ligne. Alors, comment dois-je changer le code?

Modifier: les fichiers ne doivent pas être édités, mais de nouveaux fichiers doivent être créés

Réponses

3 anubhava Nov 26 2020 at 17:31

Son utilisation gnu-awkest plus facile de cette façon en utilisant RScomme <key>chaîne:

awk -v RS='<[^>]+>' '{ ORS="" }  # init ORS to ""
RT {                                        # when RT is set
   if (!(RT in freq))                       # if RT is not in freq array
      freq[RT] = n++                        # save n in freq & increment n
   ORS="<" freq[RT] ">"                     # set ORS to < + n + >
}
{
   print $0 > ("/tmp/" FILENAME)
}' file_{a,b}.txt
1 EdMorton Nov 26 2020 at 17:24

En utilisant n'importe quel awk:

$ cat tst.awk FNR == 1 { close(out) out = FILENAME ".tmp" } { head = "" tail = $0
    while ( match(tail,/<[^>]+>/) ) {
        tgt = substr(tail,RSTART+1,RLENGTH-2)
        if ( !(tgt in map) ) {
            map[tgt] = cnt++
        }
        head = head substr(tail,1,RSTART) map[tgt]
        tail = substr(tail,RSTART+RLENGTH-1)
    }
    print head tail > out
}

$ head file_*.tmp
==> file_a.txt.tmp <==
123abc<0>xyz
efg
<1>ah
a<2>, <0>
<2>b
(<0>, <1>)

==> file_b.txt.tmp <==
xyz(<2>, <1>)
xyz<1>xy<0>z