Fjern linjen som inneholder en viss streng og følgende linje

Jeg bruker denne

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

for å fjerne linjer som inneholder strengen bar i filen.

Jeg vil imidlertid fjerne disse linjene og linjen direkte etter det . Gjerne i sed, awk eller annet verktøy som er tilgjengelig i MinGW32.

Det er en slags omvendt av hva jeg kan få i grep med -A og -B for å skrive ut matchende linjer også som linjer før / etter samsvarende linje.

Er det noen enkel måte å oppnå det på?

Kommentarer

  • Bare for informasjon: Jeg ‘ analyserer logger der oppføringene er to-liners. Så jeg vil finne en oppføring som samsvarer med mønsteret og fjerne den så vel som neste linje. Derfor trenger jeg ikke ‘ å håndtere sammenhengende kamplinjer, men takk uansett for fullstendigheten av svarene dine!

Svar

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

sed "/bar/,+1 d" 

Hvis du har bar på to påfølgende linjer, dette vil slette den andre linjen uten å analysere den. Hvis du for eksempel har en 3-linjers fil bar / bar / foo, foo linjen blir værende.

Kommentarer

  • +1 for lengden 🙂 Spesielt eksempel har jeg ‘ t sammenhengende bar s, så denne er superenkel å huske.
  • sed '/bar/d' hvis du bare vil » Fjern linjen som inneholder en bestemt streng » og ikke den neste.
  • Hvis jeg vil fjerne alle linjene etter matematikk da?
  • @Pandya At ‘ er annerledes. Du kan bruke f.eks. sed '/math/q'
  • @ A.K. Hvis du bare vil slette den samsvarende linjen, er den ‘ enda enklere: sed '/bar/d'

Svar

Hvis bar kan forekomme på påfølgende linjer, kan du gjøre:

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

som kan tilpasses for å slette mer enn to linjer ved å endre 2 ovenfor med antall linjer som skal slettes inkludert den tilsvarende.

Hvis ikke, er det «gjøres enkelt i sed med @MichaelRollins» løsning eller:

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

Kommentarer

  • Det andre pluss i AWK-løsningen er at jeg kan erstatte /bar/ /bar|baz|whatever/. I sed virker den syntaksen ikke ‘.
  • @ jakub.g har jeg GNU sed ( v4.4 nå). Ikke sikker på om de andre. Det jeg vet er at den bruker » grunnleggende » syntaks med vanlig uttrykk som standard. Dette er grunnen til at eksemplet ditt ikke ‘ fungerer ikke. For å oppnå det du vil kan du enten sette et tilbakeslag foran hver vertikale linje, eller du kan be sed om å bruke » utvidet » vanlige uttrykk. Mer informasjon her: gnu.org/software/sed/manual/html_node/… . Vær oppmerksom på at dette også gjelder grep. Her er ‘ mitt eget arbeidseksempel: echo $'0a\n1b\n2c' | sed '/0a\|1b/d'.

Svar

Jeg er ikke flytende i sed, men det er lett å gjøre det i awk:

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

The awk-skript lyder: for en linje som inneholder bar, få neste linje (getline), og hopp over all påfølgende behandling (neste). 1-mønsteret på slutten skriver ut de resterende linjene.

Oppdatering

Som påpekt i kommentaren, fungerte løsningen ovenfor ikke med påfølgende bar. Her er en revidert løsning som tar den i betraktning:

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

Vi fortsetter å lese for å hoppe over alle / bar / linjer.

Kommentarer

  • For å replikere grep -A 100%, må du også håndtere et hvilket som helst antall påfølgende bar linjer riktig (ved å fjerne hele blokken og 1 linje etter).

Svar

Du vil bruke sed «s skriptfunksjoner for å oppnå dette.

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

Eksempeldata:

$ cat sample1.txt foo bar biz baz buz 

Kommandoen «N» legger til neste linje med inndata i mønsterområdet. Dette kombinert med linjen fra mønstermatchen (/ bar /) vil være linjene du vil slette. Du kan deretter slett normalt med «d» -kommandoen.

Kommentarer

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

Svar

Hvis en linje umiddelbart etter en kamp skal fjernes, må sed -programmet vurdere sammenhengende kamper. Med andre ord, hvis du fjerner en linje etter en kamp som også samsvarer, bør du sannsynligvis fjerne linjen som følger den også.

Den er implementert ganske enkelt – men du må se litt bakover .

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 fungerer ved å bytte mellom mellomrom og mellomrom for hver linje som leses inn – slik at den siste linjen kan sammenlignes med gjeldende hver gang. Så når sed leser en linje, bytter den innholdet i bufferne – og den forrige linjen er da innholdet i redigeringsbufferen, mens den nåværende linjen settes i venteplass.

sed sjekker forrige linje for samsvar med match , og hvis dens ! fant ikke de to uttrykkene i { -funksjonen } kjøres. sed vil g et holdeplassen ved å overskrive mønsterrommet – som betyr at den nåværende linjen er i både hold- og mønsterområdet – og så vil den // sjekke den for samsvar med det siste kompilerte regulære uttrykket – match – og hvis det ikke match er p rinted.

Dette betyr at en linje bare skrives ut hvis den ikke match og den umiddelbart forrige linjen ikke match . Det gir også avkall på unødvendige bytter for sekvenser av match es.

Hvis du vil ha en versjon som kan slippe et vilkårlig antall linjer forekommer etter en match det vil trenge litt mer arbeid:

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 

. ..bytt ut 5 med antall linjer (inkludert samsvarende linje) som du vil fjerne …


1 2 3 4 12 13 14 21 

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *