Reputation: 51
I'll keep this short. I want to use sed to delete sets of text in a file. Basically here is an example of what I have:
textbefore
PATTERN 1
text1
PATTERN 3
text2
PATTERN 2
PATTERN 1
text3
text4
text5
PATTERN 2
textafter
What I want to happen is to PATTERN 1 through PATTERN 2 (inclusive) but ONLY if it contains PATTERN3 between the two.
This is basically what I want to see:
textbefore
PATTERN 1
text3
text4
text5
PATTERN 2
textafter
Any help would be appreciated
Upvotes: 4
Views: 412
Reputation: 58578
This might work for you (GNU sed):
sed '/PATTERN 1/,/PATTERN 2/!b;/PATTERN 1/h;//!H;/PATTERN 2/!d;g;/PATTERN 3/d' file
Treat lines not between the first two patterns as normal. For lines between the first two patterns, save those lines in the hold space and delete them except if the line is the second of the two patterns. For the last line between the two patterns, check if the third pattern is present and if it is delete those lines otherwise print as normal.
Upvotes: 0
Reputation: 204648
With GNU awk for multi-char RS and RT:
$ gawk -vRS='PATTERN 2' '{ORS=(sub(/(^|\n)PATTERN 1.*PATTERN 3.*/,"")?"":RT)}1' file
textbefore
PATTERN 1
text3
text4
text5
PATTERN 2
textafter
or with any awk:
$ awk '
{ buf = buf $0 RS }
/PATTERN 2/ { sub(/PATTERN 1.*PATTERN 3.*/,"",buf); printf "%s",buf; buf="" }
END { printf "%s",buf }
' file
textbefore
PATTERN 1
text3
text4
text5
PATTERN 2
textafter
Upvotes: 0
Reputation: 786289
Using perl
we have advantage of using lookarounds in regex:
perl -00 -pe 's/(?s)PATTERN 1(?=.*?PATTERN 3)((?!PATTERN [12]).)*PATTERN 2\R+//g' file
textbefore
PATTERN 1
text3
text4
text5
PATTERN 2
textafter
RegEx Explanation:
?s # to make dot match newlines also
PATTERN 1 # to match literal PATTERN 1
(?=.*?PATTERN 3) # lookahead to make sure PATTERN 3 comes before the
# next PATTERN 2
((?!PATTERN [12]).)* # negative lookahead to make sure PATTERN 1 or PATTERN 2
# isn't matched before next PATTERN 2
PATTERN 2 # to match literal PATTERN 2
\R+ # to match 1 or more new lines
Upvotes: 0
Reputation: 247210
Works with GNU sed, at least.
sed '/PATTERN 1/{:a;/PATTERN 2/bb;N;ba;:b;/PATTERN 3/d;}' file
with commentary:
#!/usr/bin/sed
/PATTERN 1/ { # when start pattern is seen
:a
/PATTERN 2/ bb # goto "b" if end pattern seen
N # append next line to pattern space
ba # and goto "a"
:b
/PATTERN 3/ d # if this "block" contains patt, delete
}
Upvotes: 2
Reputation: 41460
If you like to try awk
this should do:
awk '/PATTERN 1/ {s=1} s {a[++c]=$0} /PATTERN 3/ {p=1} /PATTERN 2/ {if (!p) for (i=1;i<=c;i++) print a[i];delete a;s=p=c=0;next} !s' file
textbefore
PATTERN 1
text3
text4
text5
PATTERN 2
textafter
How it works:
awk '
/PATTERN 1/ {s=1} # If pattern "1" is found set flag "s"
s {a[++c]=$0} # If flag "s" is set store the row in array "a"
/PATTERN 3/ {p=1} # If pattern "3" is found set flag "p"
/PATTERN 2/ { # If pattern "2" is found
if (!p) # Test if flag "p" is not set
for (i=1;i<=c;i++) # Loop "c" number of times
print a[i] # Print data in array "a"
delete a # Delete array "a"
s=p=c=0 # Reset flag and counters
next} # Skip to next line
!s # If flag "s" is not set, print the line
' file # Read the file
Upvotes: 0