Ben Lever
Ben Lever

Reputation: 2071

How do I delete a matching line, the line above and the one below it, using sed?

I have the following sequence occurring multiple times in a file:

yyyy
xxxx
zzzz

I have a regex that matches xxxx. Whenever there is a match, I want to delete that line, the line before (e.g. yyyy) and the line after it (e.g. zzzz). How can I use sed to do this?

Upvotes: 13

Views: 14407

Answers (7)

Jon
Jon

Reputation: 1241

Here is a one-line solution using ed:

ed -s input.txt <<< '/xxxx/ -1, /xxxx/ +1 d'$'\n'w

Upvotes: 1

potong
potong

Reputation: 58430

This might work for you (GNU sed):

echo -e "a\nyyyy\nxxxx\nzzzz\nb" | sed 'N;/^xxxx/M{/^xxxx/d;$!N;d};P;D'
a
b

This keeps a window of two lines in the pattern space and if the required regexp is found in either the first or second line, reads the following line and then deletes all three lines. The edge cases are if the regexp is found in either the first or last lines when there is no line before/afterward. In these cases only two lines can be deleted.

Incidentally this solution may have unearthed a possible bug in GNU sed. The M flag of an address allows the ^ and $ metacharacters to be used as zero length markers in a regexp for the start and end of line in multiline strings. The empty address // reuses a previously stated address. Should that address be one that includes a multiline flag? Presently it appears to include the flag even if it is not stated i.e.

sed 'N;/^xxxx/M{/^xxxx/d;$!N;d};P;D' file

produces a different (correct) result to:

sed 'N;/^xxxx/M{//d;$!N;d};P;D' file

if xxxx appears on the second line of a file.

Upvotes: 0

Yulian Oifa
Yulian Oifa

Reputation: 125

You can use the following:

sed -n '/xxxx/{N;s/.*//;x;d;};x;p;${x;p;}'

This will replace 3 lines with one blank line.

Upvotes: 0

ghostdog74
ghostdog74

Reputation: 342373

grep -v -f <(grep -1 "xxxx" file) file

Upvotes: -1

Beano
Beano

Reputation: 7841

The trick is to store the last line seen in the "hold space".

sed -n '
/^xxxx/{n
        n
        x
        d
       }
x
1d
p
${x
  p
 }
' <input file>

Starting with the x - swap the current input line with the hold space (x), then for the first line don't print anything (1d), subsequent lines print the line just swapped from the hold space (p), on the last line swap the hold space again and print what was in it ($x{x p}. That leaves what to do when we hit the target line (starting /^xxxx/) - read the next two lines into the pattern space (n n) and swap the pattern space with the hold space (x) - this leaves the hold space with next line we want to print and the pattern space with the line before the match, which we don't want, so we ditch it (d)

Upvotes: 15

kender
kender

Reputation: 87161

You can check out this document. It covers using sed to work with multiple lines.

Upvotes: -1

Copas
Copas

Reputation: 5952

This is how I would do it in perl maybe it can help send you on the right track... Good luck!

open(INFILE,"<in.txt");
my(@arrayOutBoundData, $skipNextLine)l
for (<INFILE>) {
    if (not $skipNextLine) {
        if (/^xxxx$/) {
            pop(@arrayOutBoundData);
            $skipNextLine = 1;
        } else {
            push(@arrayOutBoundData,$_);
        }
    }
$skipNextLine = 0
}

open(OUTFILE,">out.txt");
for (@arrayOutBoundData) {
    print OUTFILE;
}

(Not tested no perl on this system please forgive any over site.)

Upvotes: 0

Related Questions