script awk para substituir várias ocorrências de padrão de string na mesma linha em arquivos diferentes com número correspondente à string

Nov 26 2020

Eu preciso de um script awk que procure qualquer string dentro de <>, se encontrar alguma que não tenha encontrado antes, deve substituí-la pelo valor atual do contador de índice (0 no início) e incrementar o contador. Se encontrar uma string dentro de <> que já conhece, ele deve procurar o índice da string e substituí-lo pelo índice. Isso deve ser feito em vários arquivos, o que significa que o contador não zera quando vários arquivos são pesquisados ​​em busca de padrões, apenas na inicialização do programa. Por exemplo: 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

Deve se tornar

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

O que consegui até agora:

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

Mas isso só pode detectar um <> padrão por linha, mas pode haver vários <> padrões por linha. Então, como devo alterar o código?

Editar: Os arquivos não devem ser editados, em vez disso, novos arquivos devem ser criados

Respostas

3 anubhava Nov 26 2020 at 17:31

Usá- gnu-awklo é mais fácil desta forma usando RScomo <key>string:

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