Verwijder regel met een bepaalde string en de volgende regel

Ik gebruik dit

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

om regels te verwijderen die de string bar in het bestand bevatten.

Ik zou echter die regels en de regel direct willen verwijderen erna . Bij voorkeur in sed, awk of een andere tool die beschikbaar is in MinGW32.

Het is een soort omgekeerde van wat ik kan krijgen in grep met -A en -B om ook overeenkomende regels af te drukken als regels voor / na de overeenkomende regel.

Is er een gemakkelijke manier om dit te bereiken?

Opmerkingen

  • Alleen voor informatie: ik ‘ m analyseer logboeken waarin vermeldingen twee-liners zijn. Dus ik wil een item vinden dat overeenkomt met het patroon en dit verwijderen, evenals de volgende regel. Daarom hoef ik ‘ niet opeenvolgende wedstrijdregels te verwerken, maar toch bedankt voor de volledigheid van uw antwoorden!

Antwoord

Als je GNU sed hebt (dus niet-embedded Linux of Cygwin):

sed "/bar/,+1 d" 

Als je bar op twee opeenvolgende regels hebben, dit zal de tweede regel verwijderen zonder deze te analyseren. Als u bijvoorbeeld een bestand van 3 regels heeft bar / bar / foo, de regel foo blijft staan.

Reacties

  • +1 voor de lengte 🙂 In mijn bijzonder voorbeeld Ik heb ‘ geen opeenvolgende bar s, dus deze is super gemakkelijk te onthouden.
  • sed '/bar/d' als je alleen ” regel wilt verwijderen die een bepaalde string bevat ” en niet de volgende.
  • Als ik alle regels na wiskunde wil verwijderen, dan?
  • @Pandya Dat ‘ is anders. U kunt b.v. sed '/math/q'
  • @ A.K. Als u alleen de overeenkomende regel wilt verwijderen, is deze ‘ nog eenvoudiger: sed '/bar/d'

Answer

Als bar kan voorkomen op opeenvolgende regels, zou je het volgende kunnen doen:

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

die kan worden aangepast om meer dan 2 regels te verwijderen door de 2 hierboven te wijzigen met het aantal te verwijderen regels inclusief de overeenkomende.

Zo niet, dan “s gemakkelijk te doen in sed met @MichaelRollins” -oplossing of:

sed "/bar/,/^/d" < infile > outfile 

Reacties

  • Het andere pluspunt van de AWK-oplossing is dat ik /bar/ kan vervangen door /bar|baz|whatever/. In sed lijkt die syntaxis ‘ niet te werken.
  • @ jakub.g, ik heb GNU sed ( v4.4 nu). Niet zeker van de anderen. Wat ik weet is dat het standaard ” basic ” syntaxis voor reguliere expressies gebruikt. Dit is waarom uw voorbeeld ‘ t werk. Om te bereiken wat u wilt, kunt u een backslash voor elke verticale lijn plaatsen of u kunt sed vragen om ” extended ” reguliere expressies. Meer informatie hier: gnu.org/software/sed/manual/html_node/… . Houd er rekening mee dat dit ook van toepassing is op grep. Hier ‘ is mijn eigen werkvoorbeeld: echo $'0a\n1b\n2c' | sed '/0a\|1b/d'.

Antwoord

Ik spreek sed niet vloeiend, maar het is gemakkelijk om dit in awk te doen:

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

De awk-script luidt: voor een regel die een balk bevat, haalt u de volgende regel op (getline) en slaat u alle volgende bewerkingen over (volgende). Het 1-patroon aan het einde drukt de resterende regels af.

Update

Zoals aangegeven in de opmerking, werkte de bovenstaande oplossing niet met opeenvolgende bar. Hier is een herziene oplossing, die er rekening mee houdt:

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

We blijven nu lezen om alle / bar / regels over te slaan.

Opmerkingen

  • Om grep -A 100% te repliceren, moet u ook een willekeurig aantal opeenvolgende bar regels correct (door het hele blok en 1 regel erna te verwijderen).

Antwoord

U zult de scriptmogelijkheden van sed willen gebruiken om dit te bereiken.

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

Voorbeeldgegevens:

$ cat sample1.txt foo bar biz baz buz 

Het “N” -commando voegt de volgende invoerregel toe aan de patroonruimte. Dit gecombineerd met de lijn uit de patroonovereenkomst (/ bar /) zullen de lijnen zijn die u wilt verwijderen. U kunt dan verwijder normaal met het “d” commando.

Reacties

  • Hoe typ ik een nieuwe regel in de console? Of is dit alleen script?
  • @ jakub.g: met GNU sed: sed -e '/bar/{N;d}' sample1.txt

Antwoord

Als een regel direct na een match verwijderd moet worden, dan zal je sed programma opeenvolgende matches moeten overwegen. Met andere woorden, als u een regel verwijdert die volgt op een overeenkomst die ook overeenkomt, dan moet u waarschijnlijk de volgende regel ook verwijderen.

Het is eenvoudig genoeg geïmplementeerd – maar u moet een beetje achterom kijken .

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  

Het werkt door de hold- en patroonruimten voor elke ingelezen regel om te wisselen – zodat de laatste regel elke keer met de huidige kan worden vergeleken. Dus als sed een regel leest, wisselt het de inhoud van zijn buffers uit – en de vorige regel is dan de inhoud van zijn bewerkingsbuffer, terwijl de huidige regel in de wachtruimte wordt geplaatst.

Dus sed controleert de vorige regel op een overeenkomst met match , en als zijn ! niet gevonden de twee expressies in de { functie } worden uitgevoerd. sed zal g de hold-ruimte etsen door de patroonruimte te overschrijven – wat betekent dat de huidige regel zich dan in zowel de hold- als de patroonruimte bevindt – en dan zal het // controleren of het overeenkomt met de meest recent gecompileerde reguliere expressie – match – en als het niet match het p rint.

Dit betekent dat een regel alleen wordt afgedrukt als deze niet match en de direct vorige regel niet match . Het ziet ook af van onnodige swaps voor reeksen match es.

Als je een versie wilde die een willekeurig aantal regels zou kunnen laten vallen optreden na een match zou het wat meer werk vergen:

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 

. .. vervang de 5 door het aantal regels (inclusief de overeenkomende regel) dat u wilt verwijderen …


1 2 3 4 12 13 14 21 

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *