Ich habe eine Datei, die Zeilen enthält als
proto=tcp/http sent=144 rcvd=52 spkt=3
proto=tcp/https sent=145 rcvd=52 spkt=3
proto=udp/dns sent=144 rcvd=52 spkt=3
Ich brauche den Wert von Proto zu extrahieren , das ist tcp/http
, tcp/https
, udp/dns
.
Bisher habe ich dies versucht grep -o 'proto=[^/]*/'
, konnte aber nur den Wert als extrahieren proto=tcp/
.
sed
,awk
oderperl
nichtgrep
.Antworten:
Angenommen, dies hängt mit Ihrer vorherigen Frage zusammen , gehen Sie den falschen Weg. Anstatt zu versuchen, Teile von Skripten zusammenzusetzen, die die meiste Zeit irgendwie das tun, was Sie wollen, und jedes Mal ein völlig anderes Skript benötigen, wenn Sie etwas anderes tun müssen, erstellen Sie einfach 1 Skript, das Ihre analysieren kann Geben Sie die Datei in ein Array (
f[]
unten) ein, das Ihre Feldnamen (Tags) ihren Werten zuordnet. Anschließend können Sie mit dem Ergebnis alles tun, was Sie möchten, z. B. anhand dieser Eingabedatei aus Ihrer vorherigen Frage:Wir können ein awk-Skript schreiben, das ein Array der Werte erstellt, die durch ihre Namen / Tags indiziert sind:
und vorausgesetzt, Sie können mit Ihren Daten tun, was Sie möchten, indem Sie sie nur anhand der Feldnamen referenzieren, z. B. mit GNU awk,
-e
um das Mischen eines Skripts in einer Datei mit einem Befehlszeilenskript zu vereinfachen:quelle
perl
kann es einfacher sein, sie zu verwenden.awk
undsed
Skripte sind in der Regel einfacher,perl
da sie im Wesentlichen eine Obermenge von ihnen sind und zusätzliche Funktionen für allgemeine Aufgaben bieten.s/old/new/g
und sed ist nicht awk, also lassen Sie uns das beiseite legen. Ich bin völlig anderer Meinung, dass komplexe awk-Skripte in Perl einfacher sind. Sie können natürlich kürzer sein, aber Kürze ist kein wünschenswertes Merkmal von Software, Prägnanz ist es, und es ist äußerst selten, dass sie einen wirklichen Nutzen haben, und sie sind normalerweise weitaus schwieriger zu lesen, weshalb Leute Dinge wie zoitz.com posten / archives / 13 über Perl und bezeichnen es im Gegensatz zu awk als reine Schreibsprache. Ich würde immer noch gerne ein Perl-Äquivalent dazu sehenMit
grep -o
müssen Sie genau das finden, was Sie extrahieren möchten. Da Sie dieproto=
Zeichenfolge nicht extrahieren möchten , sollten Sie sie nicht abgleichen.Ein erweiterter regulärer Ausdruck, der entweder mit einem Schrägstrich oder einer nicht leeren alphanumerischen Zeichenfolge übereinstimmt
tcp
oder daraufudp
folgt, lautetDies auf Ihre Daten anwenden:
Um sicherzustellen, dass wir dies nur in Zeilen tun, die mit der Zeichenfolge beginnen
proto=
:Mit
sed
, alles vor dem ersten=
und nach dem ersten Leerzeichen entfernen :Um sicherzustellen, dass wir dies nur in Zeilen tun, die mit der Zeichenfolge beginnen
proto=
, können Sie denselben Vorverarbeitungsschrittgrep
wie oben einfügen oder verwendenHier unterdrücken wir die Standardausgabe mit der
-n
Option und lösen dann die Ersetzungen und einen expliziten Ausdruck der Zeile nur dann aus, wenn die Zeile übereinstimmt^proto=
.Mit
awk
den Standardfeldtrennzeichen verwendet wird , und dann die erste Feldaufspaltung auf ,=
und Drucken des zweiten Stück davon:Um sicherzustellen, dass wir dies nur in Zeilen tun, die mit der Zeichenfolge beginnen
proto=
, können Sie denselben Vorverarbeitungsschrittgrep
wie oben einfügen oder verwendenquelle
Wenn Sie sich auf GNU grep befinden (für die
-P
Option), können Sie Folgendes verwenden:Hier stimmen wir mit der
proto=
Zeichenfolge überein , um sicherzustellen, dass wir die richtige Spalte extrahieren, aber dann verwerfen wir sie mit dem\K
Flag aus der Ausgabe .Das Obige setzt voraus, dass die Spalten durch Leerzeichen getrennt sind. Wenn Tabulatoren auch ein gültiges Trennzeichen sind, würden Sie
\S
die Nicht-Leerzeichen verwenden, sodass der Befehl wie folgt lautet:Wenn Sie auch vor Übereinstimmungsfeldern schützen möchten, in denen
proto=
sich eine Teilzeichenfolge befindet, z. B. athisisnotaproto=tcp/https
, können Sie eine Wortgrenze mit folgender Adresse hinzufügen\b
:quelle
grep -oP 'proto=\K\S+'
. Demproto=tcp/http
kann anstelle von Leerzeichen eine Registerkarte folgen, die im\S
Gegensatz[^ ]
zu allen Nicht-Leerzeichen übereinstimmt.-o
ist auch ein GNUismus.-P
wird von GNU nur unterstützt,grep
wenn es mit PCRE-Unterstützung erstellt wurde (optional zur Erstellungszeit).Verwenden von
awk
:$1 ~ "proto"
wird sicherstellen, dass wir nur in Zeilen mitproto
in der ersten Spalte handelnsub(/proto=/, "")
wirdproto=
von der Eingabe entferntprint $1
druckt die verbleibende Spaltequelle
Code Golf auf den
grep
Lösungenoder auch
quelle
Verwenden des
cut
Befehls:quelle
http
und eindns
.Nur eine andere
grep
Lösung:Und eine ähnliche, bei der
sed
nur die übereinstimmende erfasste Gruppe gedruckt wird:quelle
Ein anderer
awk
Ansatz:Dadurch wird das Feldtrennzeichen von awk auf entweder
=
oder ein Leerzeichen gesetzt. Wenn die Zeile mit a übereinstimmt , drucken Sie=
entwederud
odertc
gefolgt von ap
das 2. Feld.Ein anderer
sed
Ansatz (nicht für alle Versionen vonsed
GNU portierbar , funktioniert aber mit GNUsed
):Das
-n
bedeutet "nicht drucken" und-E
aktiviert erweiterte reguläre Ausdrücke, die uns\S
für "Nicht-Leerzeichen",+
für "ein oder mehrere" und die Klammern für die Erfassung geben. Schließlich/p
wird sed am Ende nur dann eine Zeile drucken, wenn der Vorgang erfolgreich war, wenn also eine Übereinstimmung für den Substitutionsoperator vorliegt.Und ein Perl:
Das
-n
bedeutet "Lesen Sie die Eingabedatei Zeile für Zeile und wenden Sie das von-e
jeder Zeile angegebene Skript an". Das-l
fügt jedemprint
Anruf eine neue Zeile hinzu (und entfernt bestehende Zeilen aus der Eingabe). Das Skript selbst druckt die längste Strecke von Nicht-Leerzeichen nach aproto=
.quelle
-E
wird immer portabler, ist es aber\S
nicht.[^[:space:]]
ist ein tragbareres Äquivalent.Hier ist eine andere Lösung ganz einfach:
quelle
grep
passt zu nichts.[tc,ud]\*\\/.*
sucht nach einem Vorkommen von entwedert
oderc
oder,
oderu
oderd
, gefolgt von einem wörtlichen*
Zeichen, dann einemp
und einem Backslash. Du hast es wahrscheinlich gemeintgrep -Eo '(tc|ud)p/.* ' file | awk '{print $1}'
. Aber wenn Sie awk verwenden, können Sie das Ganze auch in awk ausführen :awk -F'[= ]' '/(tc|ud)p/{print $2}' file
.[tc,ud]p
bedeutet „eine dert
,c
,,
,u
oderd
durch eine gefolgtp
. So ist es hier nur Spiele , datcp
hatcp
undudp
hatdp
. Es wäre aber auch passen,p
odertp
auch usw., jetzt , dass Sie das haben*
, wird es passenppp
auch (die*
bedeutet "0 oder mehr", damit es auch dann übereinstimmt, wenn es nicht übereinstimmt. Sie möchten keine Zeichenklasse ([ ]
), was Sie möchten, ist eine Gruppe:(tc|ud)
(mit dem-E
Flag von verwendengrep
). Auch das.*
macht es passen Sie die gesamte Linie an.\*
, damit die ersten*
in ihrem Befehl als * und nicht als kursiv markiert wurden. Wenn Sie den Befehl in das Codeformat versetzen, wird das\
vor dem*
Erscheinen angezeigt (wodurch der Befehl fehlschlägt). Wenn Sie die Beiträge anderer Personen bearbeiten, achten Sie bitte darauf, das Erscheinungsbild des Beitrags wie folgt zu ändern.ppp
. Natürlich haben Sie Recht , dass es passen,p
odertp
- oderuucp
,ttp
,cutp
,ductp
oderd,up
.quelle
Schnittoptionen:
-f
- Feld-d
- Begrenzerquelle