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