Zeigen Sie die gesamte Datei bis zum Match an

71

grep --before-context 5 zeigt 5 Zeilen vor dem Spiel.

Ich möchte alles vor dem Spiel zeigen.
Das grep --before-context 99999999würde funktionieren, aber es ist nicht sehr ... professionell.

Wie zeige ich die gesamte Datei bis zum Spiel an?

Nicolas Raoul
quelle

Antworten:

95

Sed ist besser dafür.

Mach einfach:

sed '/PATTERN/q' FILE

Das funktioniert so:

Wir prüfen für jede Zeile, ob sie übereinstimmt /PATTERN:

  • wenn ja, drucken wir es aus und beenden es
  • ansonsten drucken wir es aus

Dies ist die effizienteste Lösung, da PATTERNsie beendet wird, sobald sie angezeigt wird. Ohne qwürde sed den Rest der Datei weiterhin lesen und nichts damit anfangen. Bei großen Dateien kann dies einen Unterschied machen.

Dieser Trick kann auch verwendet werden, um Folgendes zu emulieren head:

sed 10q FILE
Mikel
quelle
Gerade versucht, gibt es nur die erste Zeile der Datei ... obwohl Übereinstimmung in Zeile 38 ist.
Nicolas Raoul
Funktioniert gut für mich. Können Sie ein Beispiel für die tatsächliche Eingabe und Ausgabe geben? Und der Befehl, den Sie ausführen, ist.
Mikel
Ich hatte Ihren Befehl vor der Bearbeitung ausprobiert. Er lautete: sed '/ PATTERN / p; q' FILE
Nicolas Raoul
7
Was soll ich tun, wenn ich die Zeile mit der Musterübereinstimmung nicht drucken möchte?
tommy.carstensen
4
@ tommy.carstensen: sed '/PATTERN/Q' FILEüberspringt die übereinstimmende Zeile. Qist eine GNU-Erweiterung, daher funktioniert sie mit keinem sed.
Alex O
37

sed kann die meisten Funktionen von grep ersetzen.

sed -n '1,/<pattern>/ p' <file>

Dies bedeutet, dass ab der ersten Zeile gedruckt wird, bis das Muster übereinstimmt.

Einige Beispiele

sed -n '/<pattern>/,$ p' <file> # from pattern to end of file
sed -n '/<pattern1>/,/<pattern2>/ p' <file> # from pattern1 to pattern2
forcefsck
quelle
3
Dieser Befehl ist gut, aber Sie können es besser machen. Auf diese Weise wird die gesamte Datei gelesen, es ist jedoch möglich, das Programm zu beenden, sobald eine Übereinstimmung gefunden wurde.
Mikel
3
Was soll ich tun, wenn ich die Zeile mit der Musterübereinstimmung nicht drucken möchte?
tommy.carstensen
34

drucke bis und mit dem match:

awk '{print} /pattern/ {exit}' filename
sed '/pattern/q' filename

drucke bis ABER OHNE das Streichholz:

awk '/pattern/ {exit} {print}' filename
sed '/pattern/Q' filename
Glenn Jackman
quelle
11
Qist cool aber gnu spezifisch afaik, sed -n '/pattern/!p;//q'wäre portabler.
don_crissti
@don_crissti: du solltest eine antwort machen, ich denke es ist eine gute (: ich bin zwar ein wenig neugierig wie es funktioniert. ich glaube !macht pzutreffende linien nicht passend pattern, aber dann //qverwirrt mich das ...
jwd
2
@don_crissti: ah, ich habe es verstanden - //bedeutet "vorheriger regulärer Ausdruck" (ich dachte, es bedeutet "mit der leeren Zeichenkette übereinstimmen"). Ich denke, eine kürzere Version der gleichen Lösung ist sed -n '/pattern/q;p:?
jwd
@jwd - das ist in der Tat kürzer. 👍
don_crissti
1

Die folgenden reinen GNU- grepMethoden sind nicht effizient.

Suchen Sie nach allem bis zur ersten Instanz von String „ foo “ in der Datei bar , mit drei greps:

grep -m 1 -B $(grep -n -m 1 foo bar | grep -o '^[0-9]*') foo bar

Matching bis zur letzten Instanz von " foo ":

grep -oPz "(?s)[^\n]*${s}.*?\n.*?foo.*?\n" bar

Hinweis: Details zum letzten grepfinden Sie in: Regex (grep) für mehrzeilige Suche erforderlich .

agc
quelle
Warum sollte man jemals 7 greps (+ pcre) verwenden wollen, wenn es nur darum geht, einen einzelnen sedAufruf auszuführen : sed 'x;/./G;//!x;/foo/p;//s/.*//;x;d'??
don_crissti
@don_crissti, dein sedCode scheint eine eigene Antwort wert zu sein oder könnte zu einem der anderen hinzugefügt werden. Zu 7 greps: Weil es keine grepAntwort gab ... (Plus, die Antwort zeigt, warum nicht.)
agc
Es ist nicht mein Code, klicke einfach darauf ... Selbst wenn das mein Code wäre, wird das Q hier nicht beantwortet, sodass ich es nicht als Antwort posten würde.
don_crissti
1

Hinzufügen zu Mikel's Antwort oben ...


Versuchen Sie Folgendes , um alle Zeilen bis zur ersten Zeile des FILEInhalts zu drucken , ohne diesePATTERN einzuschließen:

  • sed '/.*PATTERN.*/{s///;q;}' FILE

Dies stimmt mit der gesamten Zeile überein, die das Muster enthält, ersetzt es durch eine leere Zeile und wird dann beendet, ohne den Rest der Datei zu verarbeiten.


Nachsatz:

Der einfachste / klarste Weg, um zu verhindern, dass am Ende eine zusätzliche neue Zeile gedruckt wird (ohne ein anderes Tool zu verwenden), war, sed erneut auszuführen und die neue letzte Zeile zu löschen:

sed '/.*PATTERN.*/{s///;q;}' FILE | sed '$d'

... und da wir diese Zeile jetzt sowieso löschen, ist unsere bisherige Arbeit überflüssig und wir können Folgendes vereinfachen:

sed '/PATTERN/q' FILE | sed '$d'
Jim Grisham
quelle
Glenns Antwort - und mein Kommentar dort - zeigen, wie man es mit einem einzigen sedAufruf macht.
don_crissti
(Danke dafür - ich habe Ihren Kommentar zu der Antwort von agc gesehen, aber entweder den anderen verpasst oder ihn nur überflogen, weil mein Gehirn Doppel-Negative nicht mag.) Da ich dies sowohl in einem tcshals auch in einem bashAlias ​​verwendet habe, musste ich sicherstellen, dass ich hatte eine relativ prägnante einzeilige Lösung, die sowohl in Standard als auch in GNU sed(für Portabilität) funktionierte ; alle anforderungen, die ihr beitrag sehr wohl erfüllt haben könnte. Als jemand, der sed sehr selten verwendet, war meine wichtigste Anforderung etwas, das ich schnell verstehen konnte, wenn ich es in Jahren einfach bearbeiten oder neu verwenden möchte.
Jim Grisham
1

Für Menschen, die sich bei der täglichen Arbeit nur an die grundlegenden Tools erinnern und weniger elegante und weniger effiziente Lösungen akzeptieren möchten:

head -n $(grep -n pattern filename | cut -d: -f1) filename

Wenn dieser Befehl für ein Skript bestimmt ist, suche ich nach eleganteren (und möglicherweise effizienteren) Lösungen. Wenn dies ein einmaliger Befehl oder ein Wegwerfskript ist, ist es mir egal.

Lesmana
quelle
1
Gute Idee, aber drei Befehle, wenn einer reicht.
Mikel
1
Die Grundlagen zu kennen ist in der Tat sehr gut. Es ist jedoch besser, das richtige Werkzeug für den Job zu kennen.
Soulmerge
Wenn dieser Befehl für ein Skript bestimmt ist, suche ich nach eleganteren (und möglicherweise effizienteren) Lösungen. Wenn dies ein einmaliger Befehl (oder ein Wegwerf-Skript) ist, ist es mir egal.
Lesmana
0

Sie können auch eine der folgenden Optionen verwenden

tac ./test | grep -B $(cat ./test | wc -l) -m 1 'pattern'|tac 

oder

tac ./test |head -n $(tac ./test | grep -n 'pattern' | cut -d: -f1 | head -n 1)|tac

oder

tac ./test |sed ':a;N;$!ba;s/\n/'"pattern"'/g' | sed 's/'"patternpattern"'/\n/g'|head -n 1|sed 's/'"pattern"'/\n/g'|tac

Die erste Option ist der vom OP vorgeschlagenen sehr ähnlich, stellt jedoch sicher, dass vor dem Kontext genügend Zeilen angezeigt werden, indem die Zeilen in der Datei gezählt werden

Die zweite Option durchsucht die Zeilennummer der ersten Übereinstimmung (Sie können dies auch ändern, indem Sie den inneren "Kopf" ändern) und verwendet dann den Kopf für diese Nummer

Die letzte Option ersetzt alle neuen Zeilen durch die Übereinstimmung und anschließend zwei benachbarte Übereinstimmungen durch eine neue Zeile. Die Ausgabe ist eine Zeile für jeden Textblock zwischen zwei Übereinstimmungen. Danach wählt es mit 'head' die erste Zeile (die den Textblock bis zur ersten Übereinstimmung abdeckt) und übersetzt dann jede Übereinstimmung erneut in eine neue Zeile. Diese Option funktioniert nur, wenn die Datei im folgenden Format vorliegt

pattern
texttexttext
texttexttext texttexttext
texttexttexttexttexttexttexttexttext
pattern
texttexttext
pattern 
texttexttext
texttexttexttexttexttexttexttexttext

und so weiter

user122778
quelle
2
Überlegen Sie, wie diese sedBefehle funktionieren, vor allem, weil der Befehl unten ziemlich knorrig ist.
Strugee
die erste Option ist sehr ähnlich zu dem, was die OP vorgeschlagen nur macht es sicher ti genug kines vor Kontext zeigen , indem sie die Zeilen in der FILR zählen,
user122778