Wie drucke ich ein passendes Regex-Muster mit awk?

109

Mit awkmuss ich ein Wort in einer Datei finden, das einem Regex-Muster entspricht.

Ich möchte nur das Wort drucken, das mit dem Muster übereinstimmt.

Also, wenn in der Leitung, habe ich:

xxx yyy zzz

Und Muster:

/yyy/

Ich möchte nur bekommen:

yyy

EDIT: dank kurumi habe ich so etwas geschrieben:

awk '{
        for(i=1; i<=NF; i++) {
                tmp=match($i, /[0-9]..?.?[^A-Za-z0-9]/)
                if(tmp) {
                        print $i
                }
        }
}' $1

und das habe ich gebraucht :) vielen dank!

Marverix
quelle
1
@maxtaldykin Könnten Sie bitte Ihre Selbstantwort von der Frage in eine separate Antwort verschieben?
Kenorb
2
Sie müssen nicht tun tmp=match($i, /regexp);if(tmp){}, Sie sollten nur in der Lage sein, if(tmp ~ $i){}weil ~bedeutet "entspricht dem regulären Ausdruck".
JustinCB

Antworten:

148

Das ist das Grundlegende

awk '/pattern/{ print $0 }' file

Fragen Sie awknach der patternVerwendung //und drucken Sie dann die Zeile aus, die standardmäßig als Datensatz bezeichnet wird und mit $ 0 gekennzeichnet ist. Lesen Sie zumindest die Dokumentation durch .

Wenn Sie nur das übereinstimmende Wort ausdrucken möchten.

awk '{for(i=1;i<=NF;i++){ if($i=="yyy"){print $i} } }' file
kurumi
quelle
49
Da printist die Standardaktion: awk '/pattern/' filewird ausreichen.
Johnsyweb
18
@ Johnsyweb, ja, ich kenne diese Tatsache. Für einen Anfänger wie Marverix soll es visueller sein.
Kurumi
20
Ich bezweifle dein Wissen nicht. Die Informationen können jedoch für andere nützlich sein, die diese Antwort finden.
Johnsyweb
2
NB: @marverix muss etwas mehr Hausaufgaben machen, damit die forSchleife funktioniert, wenn (a) "yyy" ein regulärer Ausdruck und keine gerade Zeichenfolge ist und (b) wenn dieses "yyy" nicht mit einem ganzen Feld darin übereinstimmt ein Rekord.
Johnsyweb
8
Es wäre nicht so $i=="yyy"; es wäre $i ~ /yyy/für einen regulären Ausdruck.
JustinCB
118

Es hört sich so an, als würden Sie versuchen, das grep -oVerhalten von GNU zu emulieren . Dies geschieht, vorausgesetzt, Sie möchten nur die erste Übereinstimmung in jeder Zeile:

awk 'match($0, /regex/) {
    print substr($0, RSTART, RLENGTH)
}
' file

Hier ist ein Beispiel mit der awkImplementierung von GNU ():

awk 'match($0, /a.t/) {
    print substr($0, RSTART, RLENGTH)
}
' /usr/share/dict/words | head
act
act
act
act
aft
ant
apt
art
art
art

Lesen Sie match, substr, RSTARTund RLENGTHim awkHandbuch.

Danach möchten Sie dies möglicherweise erweitern, um mehrere Übereinstimmungen in derselben Zeile zu verarbeiten.

Johnsyweb
quelle
NB: Um diesen letzten Teil zu beantworten, sind alle benötigten Konstrukte in Kurumis und meiner Antwort enthalten .
Johnsyweb
Gute Antwort. Ich möchte hier nur eine Erklärung, weil ich faul bin. Aber deshalb benutze ich AWK!
lukas.pukenis
Was ist, wenn ich etwas mit dem Übereinstimmungsergebnis machen möchte, außer es auszudrucken? Zum Beispiel möchte ich alle Übereinstimmungen in Array hinzufügen.
Evya2005
@ evya2005: Sie können einfach den Anruf Ron Druck durch die Aufgabe ersetzen, die Sie benötigen.
Johnsyweb
es funktioniert nicht für mich. nur Druckarbeiten. Kannst du mir ein Beispiel zeigen?
Evya2005
36

gawk kann den passenden Teil jeder Zeile erhalten, indem er dies als Aktion verwendet:

{ if (match($0,/your regexp/,m)) print m[0] }

match (string, regexp [, array]) Wenn ein Array vorhanden ist, wird es gelöscht, und das nullte Element des Arrays wird auf den gesamten Teil des Strings gesetzt, der mit regexp übereinstimmt. Wenn regulärer Ausdruck Klammern enthält, werden die ganzzahlig indizierten Elemente des Arrays so festgelegt, dass sie den Teil der Zeichenfolge enthalten, der dem entsprechenden Unterausdruck in Klammern entspricht. http://www.gnu.org/software/gawk/manual/gawk.html#String-Functions

Royas
quelle
13

Wenn Sie nur an der letzten Eingabezeile interessiert sind und nur eine Übereinstimmung finden möchten (z. B. einen Teil der Zusammenfassungszeile eines Shell-Befehls), können Sie auch diesen sehr kompakten Code ausprobieren , der unter Drucken von regulären Ausdrucken übernommen wurde mit `awk`? ::

$ echo "xxx yyy zzz" | awk '{match($0,"yyy",a)}END{print a[0]}'
yyy

Oder die komplexere Version mit einem Teilergebnis:

$ echo "xxx=a yyy=b zzz=c" | awk '{match($0,"yyy=([^ ]+)",a)}END{print a[1]}'
b

Warnung: Die awk match()Funktion mit drei Argumenten existiert nur in gawk, nicht inmawk

Hier ist eine weitere nette Lösung, bei der anstelle eines regulären Regexgrep verwendet wird awk. Diese Lösung stellt geringere Anforderungen an Ihre Installation:

$ echo "xxx=a yyy=b zzz=c" | grep -Po '(?<=yyy=)[^ ]+'
b
Daniel Alder
quelle
Warum hast du "tail -n1" hinzugefügt? Das sollte ohne funktionieren, nein?
Arthur Accioly
1
@ArthurAccioly Richtig. Ich habe den Begriff verwendet, um die durchschnittliche Hin- und Rückflugzeit aus einem Ping-Anruf zu extrahieren. lustig, dass es 4 Jahre gedauert hat, um es zu entdecken;)
Daniel Alder
12

Wenn Perl eine Option ist, können Sie Folgendes versuchen:

perl -lne 'print $1 if /(regex)/' file

Fügen Sie den iModifikator hinzu, um eine Übereinstimmung ohne Berücksichtigung der Groß- und Kleinschreibung zu implementieren

perl -lne 'print $1 if /(regex)/i' file

So drucken Sie alles NACH dem Spiel aus:

perl -lne 'if ($found){print} else{if (/regex(.*)/){print $1; $found++}}' textfile

So drucken Sie das Spiel und alles nach dem Spiel:

perl -lne 'if ($found){print} else{if (/(regex.*)/){print $1; $found++}}' textfile
Chris Koknat
quelle
3

Die Verwendung von sed kann in dieser Situation auch elegant sein. Beispiel (Zeile durch übereinstimmende Gruppe "JJJ" aus Zeile ersetzen):

$ cat testfile
xxx yyy zzz
yyy xxx zzz
$ cat testfile | sed -r 's#^.*(yyy).*$#\1#g'
yyy
yyy

Relevante Handbuchseite: https://www.gnu.org/software/sed/manual/sed.html#Back_002dreferences-and-Subexpressions

Konrad Brodzik
quelle
Für Nicht-Gnu sed ist die Lösung sed -n 's/^.*\(yyy\).*$/\1/gp' < testfile
ungefähr
1
@GrigoryEntin - bsd sed funktioniert gut mit der ursprünglichen Antwort. Der von POSIX unterstützte erweiterte Regex-Schalter ist -E, aber in FreeBSD ist mindestens -r derselbe wie -E (-r wurde 2010 hinzugefügt). Wie auch immer, versuchen Sie es mit -E (gnu sed hinzugefügt -E in 4.3)
Juan
3

Außerhalb des Themas kann dies auch mit grep erfolgen. Veröffentlichen Sie es einfach hier, falls jemand nach einer grep-Lösung sucht

echo 'xxx yyy zzze ' | grep -oE 'yyy'
Zeus
quelle
Einfache Möglichkeit, es auch mit Regex zu greifen. Genau das, was ich brauchte. Vielen Dank!
Marquee
Das funktioniert bei mir; Mein Fall ist wie folgt: echo "web_port = 8080, shutdown_port = 8005" | grep -oE "web_port = [0-9] +" # return 8080
Robb Tsang
0

Wenn Sie wissen, in welcher Spalte sich der gesuchte Text / das gesuchte Muster befindet (z. B. "JJJ"), können Sie einfach diese bestimmte Spalte überprüfen, um festzustellen, ob sie übereinstimmt, und sie ausdrucken.

Beispiel: Eine Datei mit dem folgenden Inhalt ( asdf.txt )

xxx yyy zzz

Um die zweite Spalte nur zu drucken, wenn sie mit dem Muster "JJJ" übereinstimmt, können Sie Folgendes tun:

awk '$2 ~ /yyy/ {print $2}' asdf.txt

Beachten Sie, dass dies grundsätzlich auch mit jeder Zeile übereinstimmt, in der die zweite Spalte ein "JJJ" enthält, wie diese:

xxx yyyz zzz
xxx zyyyz
Kimbo
quelle