0

In vim I can use...

:g/match/-1d

...several times to delete more than one line from before a match...

...and combine it with a few calls to...

:g/match/+1d

...to delete more than one line after the match...

:g/match/-1d,+1d

...to delete before after and current lines...

can the same be done with sed?

6
  • 2
    In that fashion. -1d and +1d, no, but I'm pretty sure it can be done by adjusting the regex. Can you give us a sample file and desired output?
    – Iskustvo
    May 11 '18 at 10:39
  • 3
    Possible duplicate of How to grep-inverse-match and exclude "before" and "after" lines. The accepted answer there shows how to do it with sed. May 11 '18 at 10:53
  • Not a duplicate: The given answer also deletes the matched line itself
    – Philippos
    May 11 '18 at 11:56
  • 1
    The :g/match/-1d,+1d part of the Question does imply to me that the intention is to delete the matched line (as part of the range).
    – Jeff Schaller
    May 11 '18 at 20:18
  • 1
    @Philippos: Clearly (IMO) a typo for -1,+1d. May 11 '18 at 21:27
1

You asked for sed, but ed can do this as well:

$ seq 10 > input
$ ed -s input <<< '/5/ -2, /5/ +2 d'$'\n'w
$ cat input
1
2
8
9
10

This tells ed to silently edit the input file with two commands:

  1. starting at the line that matches the regex 5, minus two lines and ending at the line that matches the regex 5, plus two lines, delete that range

(separated with an ANSI-escaped newline $'\n')

  1. write the file back to disk
4
  • This is one of those use cases where the standard editor is more suitable than a stream editor. May 11 '18 at 16:44
  • Jeff, this needs to be done globally so the command sequence should start with a g I think (I can't test though as I have no ed here)... but even then, @AnthonyGeoghegan , this has the same problems/limitations as the sed solution below: it works as long as the ranges don't overlap. Use printf %s\\n {1..5} 5 5 {5..9} >input to produce an input sample... May 11 '18 at 16:59
  • Interesting; I see muru already came up the same idea as I just did. The question of overlapping "matches" seems to be a good one to clarify here.
    – Jeff Schaller
    May 11 '18 at 17:04
  • @leeand00 I see you accepted this answer; are you OK with the limitations that don_crissti pointed out above? I didn't test this answer with overlapping ranges. (Besides the fact that I answered with ed vs sed!)
    – Jeff Schaller
    May 11 '18 at 17:23
1

If the regions don't overlap, you can use

sed -zE 's/([^\n]*\n){2}([^\n]*match[^\n]*)(\n[^\n]*){3}/\2/g'

with GNU sed, to remove two lines before and three after the match (the values in the {}).

With general sed implementations, you don't have option -z, so you need to collect lines in the buffer with the H;1h;$!d scheme, and can't do "everything but newline" with [^\n], so you need a workaround like this:

sed -E 'H;1h;$!d;x;y/\n#/#\n/;s/([^#]*#){2}([^#]*match[^#]*)(#[^#]*){3}/\2/g;y/\n#/#\n/'

Both solutions work on the whole file in the pattern space, so don't use this for huge files.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.