Reputation: 301
Let's say I want to delete some lines after the pattern while skipping some lines in-between, and then delete the line with the pattern itself. I can do this using the following two commands:
sed -i '/PATTERN/{n;n;n;n;n;n;N;N;N;N;d}' file # match pattern, skip 5 lines, then delete 5 consecutive lines
sed -i /PATTERN/d file # delete line with pattern
But I want to do this in a single command. The problem here is obvious as the pattern change affects both expressions so the change must be specified in a single expression. Is there a way to achieve this?
UPDATE:
Example input:
...
ifeq ($(ose),)
dh_installdocs \
$(archdir)/UserManual*.pdf $(archdir)/VirtualBox*.chm \
$(addprefix $(archdir)/, LICENSE)
rm $(addprefix $(archdir)/,UserManual*.pdf VirtualBox*.chm \
LICENSE)
else
dh_installdocs \
$(archdir)/UserManual*.pdf
rm $(addprefix $(archdir)/,UserManual*.pdf)
endif
...
Example output:
...
dh_installdocs \
$(archdir)/UserManual*.pdf $(archdir)/VirtualBox*.chm \
$(addprefix $(archdir)/, LICENSE)
rm $(addprefix $(archdir)/,UserManual*.pdf VirtualBox*.chm \
LICENSE)
...
UPDATE 2:
The example file can be obtained here: http://download.virtualbox.org/virtualbox/5.0.4/VirtualBox-5.0.4.tar.bz2 (debian/rules)
Upvotes: 0
Views: 337
Reputation: 88563
Another approach:
sed '/($(ose),)/,/^endif/{/($(ose),)/d;/^else/,/^endif/d}' file
Output:
... dh_installdocs \ $(archdir)/UserManual*.pdf $(archdir)/VirtualBox*.chm \ $(addprefix $(archdir)/, LICENSE) rm $(addprefix $(archdir)/,UserManual*.pdf VirtualBox*.chm \ LICENSE) ...
Add option -i
to edit "in place".
Upvotes: 1
Reputation: 67467
awk to the rescue!
awk 's&&s--{print;next} d&&d--{next} /pattern/{d=5;s=5;next} 1'
set s for skip and d for delete, using Ed Morton's smart counters
For your pattern you need to escape special chars
awk 's&&s--{print;next} d&&d--{next} /\(\$\(ose\),\)/{d=5;s=5;next} 1'
Upvotes: 2
Reputation: 53478
If it doesn't have to be sed
I would offer that perl
can work in "sed mode" and use regular expressions along with some more complex scripting logic.
E.g. a for loop:
#!/usr/bin/env perl
use strict;
use warnings;
while ( <DATA> ) {
if ( m/ifeq\ \(\$\(ose\)\,\)/ ) {
print "". <DATA> for 1..5;
<DATA> for ( 1..5 );
}
else { print };
}
__DATA__
...
ifeq ($(ose),)
dh_installdocs \
$(archdir)/UserManual*.pdf $(archdir)/VirtualBox*.chm \
$(addprefix $(archdir)/, LICENSE)
rm $(addprefix $(archdir)/,UserManual*.pdf VirtualBox*.chm \
LICENSE)
else
dh_installdocs \
$(archdir)/UserManual*.pdf
rm $(addprefix $(archdir)/,UserManual*.pdf)
endif
...
Which you could do 'inline' like sed:
perl -i -ne 'if ( m/ifeq\ \(\$\(ose\)\,\)/ ) { print "". <> for 1..5; <> for ( 1..5 );} else {print}'
Alternatively, you can use perl's "range operator" to detect if you're between two patterns. That depends rather more on the rest of your file though (I assume 'else' ... 'endif' isn't exactly uncommon).
Upvotes: 1
Reputation: 74596
Here's a way you could do it using awk:
awk '/ifeq/ { n = 6; next } n && !--n { c = 1 } c && c++ < 6 { next }1' file
It's a bit tricky looking but the logic is as follows:
/ifeq/
matches, set the variable n
to 6 and skip the linen && !--n
is only true when n
is 1 (it is written this way so that n
is only decremented until it reaches 0)c
has been set, skip each line until c
reaches 61
at the end means that any line that has not been skipped is printed (the default action is{ print }
.Upvotes: 1