Hallo zusammen,
vielleicht kann mir einer der anwesenden awk-Spezialisten bei folgender Frage weiterhelfen. Gegeben sei folgende Regel, anwendbar auf beliebige Strings:
####### - Der Input bleibt unverändert, wenn er "null" ist oder nur aus Blanks besteht. - Ansonsten werden die letzten sechs Stellen durch einen zufälligen Text ersetzt. - Hierbei werden Großbuchstaben durch Großbuchstaben, Kleinbuchstaben durch Kleinbuchstaben und Ziffern durch Ziffern ersetzt. - Enthält der Text rechtsbündige Blanks, so bleiben diese erhalten und werden bei der Ersetzung ignoriert. - Blanks und Sonderzeichen bleiben unverändert. - Enthält der Text weniger als sechs Stellen, werden diese durch einen gleich langen zufälligen Text gemäß obigen Regeln ersetzt.
Beispiel: "WXG73H23wZ " --> "WXG75T95aC " #######
Alle relevanten Funktionen zum Ersetzen der jeweiligen Chars funktionieren bereits.
Momentan scheitere ich offenbar an der Regex, welche beim Aufruf von gensub() zwar den Inhalt der Backreference liefert, aber leider auch den kompletten String davor. Die RegEx selbst soll nur die 6 relevanten Zeichen ohne die folgenden Whitespaces extrahieren, damit diese durch die Ersetzen-Funktion weiterverarbeitet werden können.
Der folgende Aufruf liefert bei der Eingabe von "WXG73H23wZ " aber nicht wie erwartet ein "3H23wZ" sondern wieder "WXG73H23wZ "
############## sRegEx = "(.{1,6}[^\s])\s*$" print gensub(sRegEx,"\1","g",sInput) ##############
Selbst wenn ich den Quantifier ".{1,6}" durch ".?.?.?.?.?.?" ersetze, weil das nicht bei allen awk-Versionen umgesetzt ist, bleibt das Ergebnis das gleiche.
Wenn ich zum "debuggen" ein 'print gensub(sRegEx,"#\1","g",sInput)' nutze, wird stattdessen "WXG73H#23wZ " zurückgeliefert, d.h. \1 matcht offenbar - nur nicht das richtige.
Wo liegt mein Denkfehler?
Grüße /Marian
Dein "gensub" ersetzt "ab minimal 1 und maximal 6 Zeichen vor dem letzten Nicht-Leerzeichen bis zum Ende" durch "die gefundenen Zeichen". Das Ergebnis ist dann natürlich "alles vor den gefunden Zeichen" und "die gefunden Zeichen". Wenn du nur "die gefunden Zeichen" haben willst, muss dein RegEx den _ganzen_ String von Anfang an matchen:
print gensub("^.*(.{,6})\>\s*$", "\1", "g", sInput)
Oder dieses Script für die Gesamtaufgabe:
--- script --- #!/usr/bin/awk -f
function randonoise(c) { sub(/^[0-9]$/, sprintf("%c", int(rand() * 10) + 48), c); sub(/^[A-Z]$/, sprintf("%c", int(rand() * 26) + 65), c); sub(/^[a-z]$/, sprintf("%c", int(rand() * 26) + 97), c);
return c; }
{ idx = match($0, /.{,6}>\s*$/); # find position of first of at most 6 trailing characters at the end of the last word with optional tangling white space out = substr($0, 1, idx); # initialize output to everything before the match
while (idx++ < length($0)) # loop over the remaining characters out = out randonoise(substr($0, idx, 1)); # randonoise each character and append to out
print out; } --- /script ---
Hallo Kai, Danilo, Martin,
Am 21.07.2015 um 21:00 schrieb Kai Kunstmann:
Dein "gensub" ersetzt "ab minimal 1 und maximal 6 Zeichen vor dem letzten Nicht-Leerzeichen bis zum Ende" durch "die gefundenen Zeichen". Das Ergebnis ist dann natürlich "alles vor den gefunden Zeichen" und "die gefunden Zeichen". Wenn du nur "die gefunden Zeichen" haben willst, muss dein RegEx den _ganzen_ String von Anfang an matchen:
print gensub("^.*(.{,6})\>\s*$", "\1", "g", sInput)
Ein ähnliches Konstrukt hatte ich bereits erfolglos getestet. Auch deine Variante funktioniert nicht so, wie sie soll.
Sowohl Danilo als auch Martin bringen mich aber langsam auf den Gedanken, dass dieses Konstrukt mit den verfügbaren AWK-Versionen nicht läuft. Zur Auswahl stehen nur 4.0.2 (CentOS 7) oder 3.1.7 (CentOS 6). Ich teste es nochmal mit den verschiedenen Optionen --traditional, --posix und/oder --re-interval, glaube aber nicht, dass es zu besseren Ergebnissen führen wird. Zur Not muss ich einen externen Aufruf von sed machen. Nutzung von P(erl|ython|ash) fällt leider vorerst aus.
Oder dieses Script für die Gesamtaufgabe: [...]
Besten Dank für den Tipp, das teste ich mal.
Viele Grüße /Marian
Ich denke, was du versuchst, lässt sich mit RegEx ohne "lookaround" oder "reluctant qualifier" nicht in einem einzigen Aufruf umsetzten, weil du am Anfang beliebig viel Rauschen matchen musst, und der entsprechende Rausch-Matcher immer die Zeichen von deiner Ergebnis-Group auffrist. Ich hatte 'print gensub("^.*(.{,6})\>\s*$", "\1", "g", sInput)' ehrlich gesagt nicht getestet, aber jetzt sehe ich, dass und warum es nicht funktioniert.
Schau dir nochmal mein Script an. Der Trick ist, dass du dir mittels 'idx = match(sInput, ".{,6}\>\s*$")' die Start-Position der 6 Zeichen geben lässt, und dann mittels 'substr()' den richtigen Teil ausschneidest, oder wie in meinem Script einfach alle Zeichen bis zum Schluss behandelst (es folgen ja nur zu ignorierende Leerzeichen).
MfG
lug-dd@mailman.schlittermann.de