0

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)

6
  • 1
    Does it have to be sed and a single command? Can you give an example input and output for testing? But first thought would - if you're doing keyed on the same pattern, doesn't that mean you're deleting then skipping? Commented Sep 23, 2015 at 20:44
  • Yes, it has to be a one line command, preferably sed. If sed isn't an option then maybe others will do (I haven't looked for an answer outside of sed). I will update the initial post with an example. Commented Sep 23, 2015 at 20:50
  • Why is my question being downvoted? Commented Sep 23, 2015 at 21:12
  • There was an answer before suggesting to use x; after {. This partially works as it deletes everything from the line with the matching pattern while leaving a blank line. Is there a way to remove the whole line? Commented Sep 23, 2015 at 21:28
  • Please tell me what's wrong with my question so I can fix it. I'm always trying to properly formulate my questions before posting. Commented Sep 23, 2015 at 22:06

4 Answers 4

2

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' 
Sign up to request clarification or add additional context in comments.

4 Comments

How can I use ($(ose),) as the pattern?
Is it literal "($(ose),)"? Use the quoted value as a variable and change /patttern/ to $0~var
Yes, it's literal as you can see in the example. Using a variable requires an extra line so it defeats the purpose. Anyway, I wasn't able to get it working.
setting the var is for convenience and can be done with awk -v option, if you like you can embed the pattern by escaping $ ( ) as in /\(\$\(ose\),\)/
1

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".

1 Comment

Now shoot me. Such an simple and clever workaround. Why didn't I think of this before? THANK YOU countless times!!
1

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:

  • When the pattern /ifeq/ matches, set the variable n to 6 and skip the line
  • n && !--n is only true when n is 1 (it is written this way so that n is only decremented until it reaches 0)
  • When c has been set, skip each line until c reaches 6
  • 1 at the end means that any line that has not been skipped is printed (the default action is
    { print }.

6 Comments

This seems to work but how can I save the results to a file? Adding -i doesn't work.
The standard way to do that is to redirect to a temporary file then overwrite the original: awk '...' file > tmp && mv tmp file.
I already tried redirecting the output to a different file but awk hangs when I do that (i.e. waits for input).
Sounds like a syntax error to me. Using the code in my answer and assuming that the file is called file, it would be: awk '/ifeq/ { n = 6; next } n && !--n { c = 1 } c && c++ < 6 { next }1' file > tmp && mv tmp file.
This worked, however, the resulting file is not correct. Several lines are missing across the file.
|
1

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).

7 Comments

Does it mean that sed cannot be used for this?
I don't know. sed can do a lot of stuff, but I started using perl 15 years ago when I started hitting the limits of "shell tools" and haven't really looked back.
I'm getting the following error when trying your command: syntax error at -e line 1, near "else print " Execution of -e aborted due to compilation errors.
Might need the braces then - sorry, can't test that bit. Edited
Unfortunately, the change you made now doubles every line in the file for some reason.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.