skrypt awk do zamiany wielu wystąpień wzorca łańcuchowego w tej samej linii w różnych plikach na liczbę pasującą do łańcucha

Nov 26 2020

Potrzebuję skryptu awk, który wyszukuje dowolny ciąg wewnątrz <>, jeśli znajdzie taki, którego nie znalazł przedtem, powinien zastąpić go bieżącą wartością licznika indeksu (0 na początku) i zwiększyć licznik. Jeśli znajdzie łańcuch wewnątrz <>, który już zna, powinien sprawdzić indeks tego ciągu i zastąpić go indeksem. Należy to zrobić dla wielu plików, co oznacza, że ​​licznik nie resetuje się, gdy wiele plików jest przeszukiwanych w poszukiwaniu wzorców, tylko podczas uruchamiania programu Na przykład: plik_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

powinno stać się

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

Co mam do tej pory:

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

Ale to może wykryć tylko jeden wzorzec <> na linię, ale może istnieć wiele wzorców <> w linii. Jak więc mam zmienić kod?

Edycja: Pliki nie powinny być edytowane, zamiast tego powinny zostać utworzone nowe pliki

Odpowiedzi

3 anubhava Nov 26 2020 at 17:31

Używanie gnu-awkgo jest łatwiejsze w ten sposób, używając RSjako <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

Używanie dowolnego 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