Fjern linje, der indeholder en bestemt streng, og den følgende linje

Jeg bruger denne

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

for at fjerne linjer, der indeholder strengen bar i filen.

Jeg vil dog gerne fjerne disse linjer og linjen direkte efter det . Fortrinsvis i sed, awk eller andet værktøj, der er tilgængeligt i MinGW32.

Det er en slags omvendt af hvad jeg kan få i grep med -A og -B for også at udskrive matchende linjer som linjer før / efter den matchede linje.

Er der nogen nem måde at opnå det på?

Kommentarer

  • Bare til information: Jeg ‘ m analyserer logfiler, hvor poster er to-liners. Så jeg vil finde en post, der matcher mønsteret og fjerne det såvel som den næste linje. Derfor behøver jeg ikke ‘ at håndtere på hinanden følgende matchlinjer, men tak alligevel for fuldstændigheden af dine svar!

Svar

Hvis du har GNU sed (så ikke-integreret Linux eller Cygwin):

sed "/bar/,+1 d" 

Hvis du har bar på to på hinanden følgende linjer, dette sletter den anden linje uden at analysere den. Hvis du f.eks. Har en 3-linjers fil bar / bar / foo, foo -linjen forbliver.

Kommentarer

  • +1 for længden 🙂 I særdeleshed eksempel har jeg ‘ ikke sammenhængende bar s, så denne er super let at huske.
  • sed '/bar/d' hvis du bare vil ” Fjern linje, der indeholder en bestemt streng ” og ikke den næste.
  • Hvis jeg vil fjerne alle linjerne efter matematik, så?
  • @Pandya At ‘ er anderledes. Du kan f.eks. sed '/math/q'
  • @ A.K. Hvis du bare vil slette den matchende linje, er den ‘ endnu enklere: sed '/bar/d'

Svar

Hvis bar muligvis forekommer på hinanden følgende linjer, kan du gøre:

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

som kan tilpasses til at slette mere end 2 linjer ved at ændre 2 ovenfor med antallet af linjer, der skal slettes inklusive den matchende.

Hvis ikke, er det “gøres let i sed med @MichaelRollins” løsning eller:

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

Kommentarer

  • Det andet plus i AWK-løsningen er, at jeg kan erstatte /bar/ /bar|baz|whatever/. I sed synes denne syntaks ‘ ikke at fungere.
  • @ jakub.g har jeg GNU sed ( v4.4 nu). Ikke sikker på de andre. Hvad jeg ved er, at det bruger ” grundlæggende ” syntaks med regulært udtryk som standard, det er derfor dit eksempel ikke ‘ fungerer ikke. For at opnå det, du ønsker, kan du enten sætte et tilbageslag foran hver lodrette linje, eller du kan bede sed om at bruge ” udvidet ” regulære udtryk. Flere oplysninger her: gnu.org/software/sed/manual/html_node/… . Bemærk, at dette også gælder for grep. Her er ‘ mit eget arbejdseksempel: echo $'0a\n1b\n2c' | sed '/0a\|1b/d'.

Svar

Jeg taler ikke flydende, men det er let at gøre det i awk:

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

awk-script lyder: for en linje, der indeholder bjælke, skal du hente den næste linje (getline) og derefter springe al efterfølgende behandling over (næste). 1 mønsteret i slutningen udskriver de resterende linjer.

Opdatering

Som påpeget i kommentaren fungerede ovenstående løsning ikke sammenhængende bar. Her er en revideret løsning, der tager den i betragtning:

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

Vi fortsætter med at læse for at springe over alle / bar / linjer.

Kommentarer

  • For at replikere grep -A 100% skal du også håndtere et vilkårligt antal på hinanden følgende bar linjer korrekt (ved at fjerne hele blokken og 1 linje efter).

Svar

Du vil gerne bruge sed “s scripting-muligheder for at opnå dette.

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

Eksempeldata:

$ cat sample1.txt foo bar biz baz buz 

Kommandoen “N” tilføjer den næste inputlinje i mønsterområdet. Dette kombineret med linjen fra mønstermatch (/ bar /) vil være de linjer, du vil slette. Du kan derefter slet normalt med kommandoen “d”.

Kommentarer

  • Hvordan skriver jeg en ny linje i konsollen? Eller er dette kun script?
  • @ jakub.g: med GNU sed: sed -e '/bar/{N;d}' sample1.txt

Svar

Hvis en linje umiddelbart efter en kamp skal fjernes, skal dit sed -program overveje på hinanden følgende kampe. Med andre ord, hvis du fjerner en linje efter et match, som også matcher, bør du sandsynligvis også fjerne linjen, der følger den.

Den implementeres simpelthen nok – men du skal se lidt bagud .

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  

Det virker ved at bytte mellemrum for hold og mønster for hver linje, der læses ind – så den sidste linje kan sammenlignes med den aktuelle hver gang. Så når sed læser en linje, udveksler den indholdet af bufferne – og den forrige linje er derefter indholdet af dens redigeringsbuffer, mens den aktuelle linje sættes i holdrum.

sed kontrollerer den foregående linje for et match til match , og hvis dens ! fandt ikke de to udtryk i { -funktionen } køres. sed vil g et holdrummet ved at overskrive mønsterområdet – hvilket betyder, at den aktuelle linje derefter er i både hold og mønsterrum – og så vil det // kontrollere det for et match med det senest sammensatte regulære udtryk – match – og hvis det ikke match er p rinted.

Dette betyder, at en linje kun udskrives, hvis den ikke match og den umiddelbart forrige linje ikke match . Det giver også afkald på unødvendige swaps til sekvenser af match es.

Hvis du vil have en version, der kan slippe et vilkårligt antal linjer forekommer efter en match det vil have brug for lidt mere arbejde:

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 

. ..placer 5 med antallet af linjer (inklusive den matchede linje) som du gerne vil fjerne …


1 2 3 4 12 13 14 21 

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *