Reputation: 2071
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
Reputation: 1241
Here is a one-line solution using ed
:
ed -s input.txt <<< '/xxxx/ -1, /xxxx/ +1 d'$'\n'w
Upvotes: 1
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
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
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
Reputation: 87161
You can check out this document. It covers using sed
to work with multiple lines.
Upvotes: -1
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