Entfernen Sie die Zeile mit einer bestimmten Zeichenfolge und die folgende Zeile

71

ich benutze das

cat foo.txt | sed '/bar/d'

um Zeilen zu entfernen, die den String barin der Datei enthalten.

Ich möchte jedoch diese Zeilen und die Zeile direkt danach entfernen . Vorzugsweise in sed, awkoder einem anderen Werkzeug , das in MinGW32 vorhanden ist.

Es ist eine Art Gegenteil das , was ich in kann grepmit -Aund -BLinien sowie Linien zu drucken , passend vor / nach der angepassten Linie.

Gibt es einen einfachen Weg, dies zu erreichen?

jakub.g
quelle
2
Nur zur Information: Ich analysiere Protokolle, in denen Einträge aus zwei Zeilen bestehen. Ich möchte also einen Eintrag finden, der zum Muster passt, und ihn sowie die nächste Zeile entfernen. Daher muss ich keine aufeinanderfolgenden Übereinstimmungszeilen bearbeiten, aber trotzdem danke für die Vollständigkeit Ihrer Antworten!
jakub.g 20.11.12

Antworten:

75

Wenn Sie GNU sed haben (also nicht eingebettetes Linux oder Cygwin):

sed '/bar/,+1 d'

Wenn Sie barzwei aufeinanderfolgende Zeilen haben, wird die zweite Zeile gelöscht, ohne sie zu analysieren. Zum Beispiel, wenn Sie eine 3-Zeilen - Datei bar/ bar/ foo, die foowird Linie bleiben.

Gilles
quelle
1
+1 für die Länge :) In meinem speziellen Beispiel habe ich keine aufeinanderfolgenden bars, daher ist dieses super einfach zu merken.
jakub.g 20.11.12
11
sed '/bar/d'wenn Sie nur die Zeile mit einer bestimmten Zeichenfolge entfernen möchten und nicht die nächste.
AJP
Wenn ich dann nach Mathe alle Zeilen entfernen möchte?
Pandya
1
@ Pandya Das ist anders. Sie können zBsed '/math/q'
Gilles
1
@AK Wenn Sie nur die übereinstimmende Zeile löschen möchten, ist es noch einfacher:sed '/bar/d'
Gilles
16

Wenn bardies in aufeinanderfolgenden Zeilen vorkommen kann, können Sie Folgendes tun:

awk '/bar/{n=2}; n {n--; next}; 1' < infile > outfile

Dies kann angepasst werden, um mehr als 2 Zeilen zu löschen, indem die 2 oben durch die Anzahl der zu löschenden Zeilen einschließlich der übereinstimmenden geändert wird.

Wenn nicht, ist dies sedmit der Lösung von @MichaelRollins einfach zu erledigen oder:

sed '/bar/,/^/d' < infile > outfile
Stéphane Chazelas
quelle
Der andere Plus in der AWK - Lösung ist , dass ich ersetzen /bar/mit /bar|baz|whatever/. In seddieser Syntax scheint nicht zu funktionieren.
jakub.g
@ jakub.g, ich habe GNU sed (v4.4 jetzt). Ich bin mir nicht sicher, was die anderen angeht. Was ich weiß ist, dass es "grundlegende" Syntax für reguläre Ausdrücke verwendet. Aus diesem Grund hat Ihr Beispiel nicht funktioniert. Um das zu erreichen, was Sie wollen, können Sie entweder einen Backslash vor jede vertikale Linie setzen oder sednach "erweiterten" regulären Ausdrücken fragen . Weitere Informationen finden Sie hier: gnu.org/software/sed/manual/html_node/… . Bitte beachten Sie, dass dies auch für gilt grep. Hier ist mein eigenes Arbeitsbeispiel: echo $'0a\n1b\n2c' | sed '/0a\|1b/d'.
Victor Yarema
12

Ich spreche nicht fließend sed, aber es ist einfach, dies in awk zu tun:

awk '/bar/{getline;next} 1' foo.txt 

Das awk-Skript lautet: Für eine Zeile, die einen Balken enthält, holen Sie sich die nächste Zeile (getline) und überspringen Sie dann die gesamte nachfolgende Verarbeitung (next). Das 1-Muster am Ende druckt die restlichen Zeilen.

Aktualisieren

Wie in dem Kommentar ausgeführt, funktionierte die obige Lösung nicht aufeinanderfolgend bar. Hier ist eine überarbeitete Lösung, die dies berücksichtigt:

awk '/bar/ {while (/bar/ && getline>0) ; next} 1' foo.txt 

Wir lesen jetzt weiter, um alle / Balken / Zeilen zu überspringen.

Hai Vu
quelle
1
Um grep -A100% zu replizieren , müssen Sie auch eine beliebige Anzahl aufeinanderfolgender barZeilen korrekt behandeln (indem Sie den gesamten Block und eine Zeile danach entfernen).
jw013
7

Sie werden die Skriptfunktionen von sed nutzen wollen, um dies zu erreichen.

$ sed -e '/bar/ { 
 $!N
 d
 }' sample1.txt

Beispieldaten:

$ cat sample1.txt 
foo
bar
biz
baz
buz

Mit dem Befehl "N" wird die nächste Eingabezeile in den Musterraum eingefügt. In Kombination mit der Zeile aus der Musterübereinstimmung (/ bar /) werden die Zeilen angezeigt, die Sie löschen möchten. Sie können dann normal mit dem Befehl "d" löschen.

Michael Rollins
quelle
Wie schreibe ich einen Zeilenumbruch in die Konsole? Oder ist dies nur ein Skript?
jakub.g 20.11.12
@ jakub.g: mit GNU sed:sed -e '/bar/{N;d}' sample1.txt
Cyrus
2

Wenn eine Zeile, die unmittelbar auf eine Übereinstimmung folgt, entfernt werden soll, muss Ihr sedProgramm aufeinanderfolgende Übereinstimmungen berücksichtigen. Mit anderen Worten, wenn Sie eine Zeile nach einer Übereinstimmung entfernen, die auch übereinstimmt, sollten Sie wahrscheinlich auch die darauf folgende Zeile entfernen.

Es ist einfach genug implementiert - aber man muss ein wenig nachschauen.

printf %s\\n     0 match 2 match match \
                 5 6 match match match \
                 10 11 12 match 14 15  |
sed -ne'x;/match/!{g;//!p;}'

0
6
11
12
15

Dabei werden Halte- und Musterbereiche für jede eingelesene Zeile vertauscht, sodass die letzte Zeile jedes Mal mit der aktuellen verglichen werden kann. Wenn also sedeine Zeile gelesen wird, tauscht sie den Inhalt ihrer Puffer aus - und die vorherige Zeile ist dann der Inhalt ihres Editierpuffers, während die aktuelle Zeile in den Hold-Space gestellt wird.

So sedprüft die vorhergehende Zeile für ein Spiel zu match, und wenn sie !nicht die beiden Ausdrücke in der gefundene {Funktion }ausgeführt werden . sedwird get der Halteraum , der durch den Musterraum überschrieben wird - was die aktuelle Zeile bedeutet , ist dann sowohl in den Griff und Musterräume - und dann wird es //es für ein Spiel seiner zuletzt kompilierten regulären Ausdruck überprüfen - match- und wenn es nicht matches wird pgerintert.

Dies bedeutet, dass eine Zeile nur gedruckt wird, wenn dies nicht der Fall ist, und die unmittelbar vorhergehende Zeile nicht . Sie verzichtet auch auf unnötige Auslagerungen für Sequenzen von es.match matchmatch

Wenn Sie eine Version wünschen, die eine beliebige Anzahl von Zeilen löschen kann, die nach einem auftreten match, wäre etwas mehr Arbeit erforderlich:

printf %s\\n    1 2 3 4 match  \
                match match 8  \
                9 10 11 12 13  \
                14 match match \
                17 18 19 20 21 |
sed -net -e'/match/{h;n;//h;//!H;G;s/\n/&/5;D;}' -ep

... ersetzen Sie die 5 durch die Anzahl der Zeilen (einschließlich der übereinstimmenden Zeile) , die Sie entfernen möchten ...


1
2
3
4
12
13
14
21
mikeserv
quelle