awk-Skript zum Ersetzen mehrerer Vorkommen von Zeichenfolgenmustern in derselben Zeile in verschiedenen Dateien durch eine der Zeichenfolge entsprechende Nummer

Nov 26 2020

Ich benötige ein awk-Skript, das nach einer Zeichenfolge in <> sucht. Wenn es eine Zeichenfolge findet, die es zuvor nicht gefunden hat, sollte es durch den aktuellen Wert des Indexzählers (0 am Anfang) ersetzt und der Zähler erhöht werden. Wenn es eine Zeichenfolge in <> findet, die es bereits kennt, sollte es den Index der Zeichenfolge nachschlagen und durch den Index ersetzen. Dies sollte über mehrere Dateien hinweg erfolgen. Dies bedeutet, dass der Zähler nicht zurückgesetzt wird, wenn mehrere Dateien nach den Mustern durchsucht werden, sondern nur beim Programmstart. Beispiel: 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

soll werden

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

Was ich bisher bekommen habe:

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

Dies kann jedoch nur ein <> Muster pro Zeile erkennen, es können jedoch mehrere <> Muster pro Zeile vorhanden sein. Wie soll ich den Code ändern?

Bearbeiten: Die Dateien sollten nicht bearbeitet werden, sondern neue Dateien sollten erstellt werden

Antworten

3 anubhava Nov 26 2020 at 17:31

Die gnu-awkVerwendung RSals <key>Zeichenfolge ist auf diese Weise einfacher :

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

Mit einem beliebigen 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