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/
. Ised
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å gjeldergrep
. 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ølgendebar
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.
Så 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