script awk para reemplazar múltiples ocurrencias de patrón de cadena en la misma línea en diferentes archivos con un número que coincida con la cadena

Nov 26 2020

Necesito un script awk que busque cualquier cadena dentro de <>, si encuentra una que no ha encontrado antes, debería reemplazarla con el valor actual del contador de índice (0 al principio) e incrementar el contador. Si encuentra una cadena dentro de <> que ya conoce, debería buscar el índice de la cadena y reemplazarla con el índice. Esto debe hacerse en varios archivos, lo que significa que el contador no se restablece cuando se buscan patrones en varios archivos, solo al iniciar el programa. Por ejemplo: 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

debe convertirse

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

Lo que tengo hasta ahora:

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

Pero esto solo puede detectar un patrón <> por línea, pero puede haber múltiples patrones <> por línea. Entonces, ¿cómo debo cambiar el código?

Editar: los archivos no deben editarse, sino que deben crearse nuevos archivos

Respuestas

3 anubhava Nov 26 2020 at 17:31

Usarlo gnu-awkes más fácil de esta manera usando RScomo <key>cadena:

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

Usando cualquier 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