![Gensub auf mehreren Zeilen](https://rvso.com/image/76424/Gensub%20auf%20mehreren%20Zeilen.png)
Ich habe eine Datei, die viele zufällige Zeilen enthält wie
aaa bbb
ccc ddd
eee mark: 98 fff
ggg ggg jjjj iii
jjj kkkk
Ich möchte awk UND nur gensub verwenden, um die Zahl „98“ oben abzugleichen. Bisher habe ich den folgenden Code, aber ich glaube, er funktioniert nicht, weil ich gensub so einstellen muss, dass „\n“ wie jedes andere Zeichen behandelt wird.
cat file.txt | awk 'printf(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'
Ich brauche als Ausgabe des obigen Codes nur „98“. Wie mache ich das?
BEARBEITEN
selbst wenn ich den Modifikator „s“ oder „m“ verwende, funktioniert es nicht wie es sollte, da meines Wissens der Modifikator „s“ dazu führen sollte, dass reguläre Ausdrücke . als jedes Zeichen einschließlich \n behandeln.
Antwort1
Sie scheinen zu glauben, dass awk
die Eingabe als mehrzeilige Zeichenfolge behandelt wird. Das ist nicht der Fall. Wenn Sie ein awk-Skript auf einer Datei ausführen, wird das Skript angewendet.zu jeder Zeile der Dateiseparat. Ihr gensub
wurde also einmal pro Zeile ausgeführt. Sie können damit eigentlich machen, was Sie wollen, awk
aber es ist wirklich nicht das beste Werkzeug für diese Aufgabe.
Soweit ich weiß, haben Sie eine große Datei und möchten nur eine Zahl drucken, die nach mark:
einem Leerzeichen kommt. Wenn das der Fall ist, sind alle diese Ansätze einfacher als das Herumspielen mit gensub
:
Verwendung
grep
mit Perl-kompatiblen regulären Ausdrücken (-P
)$ grep -oP 'mark:\s*\K\d+' file 98
Das
-o
bewirkt, dassgrep
nur der übereinstimmende Teil der Zeile gedruckt wird. Das\K
ist eine PCRE-Konstruktion, die bedeutet: „Alles, was vor diesem Punkt übereinstimmt, ignorieren.“sed
$ sed -n 's/.*mark:\s*\([0-9]\+\).*/\1/p' file 98
Das
-n
unterdrückt die normale Ausgabe. Dasp
am Endesed
druckt nur, wenn die Ersetzung erfolgreich war. Der reguläre Ausdruck selbst erfasst eine Zahlenfolgemark:
und 0 oder mehr Leerzeichen und ersetzt die gesamte Zeile mit dem, was erfasst wurde.Perl
$ perl -ne 'print if s/.*mark:\s*(\d+).*/$1/' file 98
Das
-n
weist Perl an, eine Eingabedatei zeilenweise zu lesen und das durch angegebene Skript anzuwenden-e
. Das Skript druckt alle Zeilen aus, bei denen die Ersetzung erfolgreich war.
Wenn Sie wirklich, wirklich verwenden möchten gensub
, könnten Sie etwas wie Folgendes tun:
$ awk '/mark:/{print gensub(/.*mark:\s*([0-9]+).*/,"\\1","g")}' file
98
Persönlich würde ich es in awk folgendermaßen machen:
$ awk '/mark:/{gsub(/[^0-9]/,"");print}' file
98
Da Sie anscheinend versucht haben, awk dazu zu bringen, mehrzeilige Eingaben zu empfangen, können Sie dies folgendermaßen erreichen (vorausgesetzt, Ihre Datei enthält keine NULL-Zeichen):
$ awk '{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}' RS='\0' file
98
Das RS='\0'
setzt den Eingabedatensatztrenner (das definiert eine „Zeile“ für awk
) auf \0
. Da Ihre Datei keine solchen Zeichen enthält, wird dadurch awk
alles auf einmal gelesen.
Antwort2
Die kleinste Änderung, damit es funktioniert, ist:
cat file | awk '/mark:/{printf( "%s\n",gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'
Das /mark:/ dient zum Auswählen einer Zeile, die "mark:" enthält.
Aber warum wird dann ein printf benötigt? Das hier funktioniert auch:
cat file | awk '/mark:/{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'
Aber das wäre ein „sinnloser Einsatz der Katze", da awk direkt aus einer Datei lesen könnte:
awk '/mark:/{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}' file
Bearbeiten:
Auf Benutzeranfrage: So verwenden Sie den regulären Ausdruck auf Datei und Zeichenfolge.
Nun, mit den von Ihnen festgelegten Regeln ist awk mit nur gensub nicht möglich.
Außerdem bedeutet die Idee, mit „matching with“ .*mark: ([0-9]+).*
alles durch das Match in den Klammern zu ersetzen, dass die ganze Datei abgeglichen werden muss, um einen Teil zu extrahieren. Das ist einer der Gründe, warum grep erstellt wurde.
Benutz einfach:
grep -oP "mark: \K([0-9]+)" file
oder:
echo "$string" | grep -oP "mark: \K([0-9]+)"
Und Sie erhalten das Ergebnis.