DMS
DMS

Reputation: 357

BASH - Selective deletion

I have a file which looks like this:

Guest-List 1
All present
Guest-list 2
All present
Guest-List 3
Guest-list 4
All present
Guest-list 5

I want to remove the line containing "All present" and its title (the line just above "All present"). The desired output would be:

Guest-List 3
Guest-list 5

I am interested in implementing this using sed. Because I am a rookie, other possible solutions without sed will be appreciated as well (when answering please provide detailed explanation so I can learn) : )

(I know can delete a line matching a regex, and could store the line above it sending it to the hold buffer, something like this: sed '/^.*present$/d; h' ... then the "g" command would copy the hold buffer back to the pattern space... but how do I tell sed to delete that as well?)

Thanks in advance!

Upvotes: 1

Views: 144

Answers (3)

Ed Morton
Ed Morton

Reputation: 204015

If you are using more than the s, g, and p (with -n) commands in sed then you are using language constructs that became obsolete in the mid-1970s when awk was invented.

sed is an excellent tool for simple substitutions on a single line, for anything else just use awk:

$ cat file
Guest-List 1
All present
Guest-list 2
All present
Guest-List 3
Guest-list 4
All present
Guest-list 5

$ awk 'NR==FNR{ if (/All present/) {skip[FNR-1]; skip[FNR]} next} !(FNR in skip)' file file
Guest-List 3
Guest-list 5

The above just parses the file twice - first time to create an array named skip of the line numbers (FNR) you do not want output, and the second time to print the lines that are not in that array. Simple, clear, maintainable, extensible, ....

Upvotes: 0

anubhava
anubhava

Reputation: 785581

You can use fgrep like this:

fgrep -v -f <(fgrep 'All present' -B1 file) file
Guest-List 3
Guest-list 5

Upvotes: 3

Ewan
Ewan

Reputation: 15058

sed -n '/All present$/{s/.*//;x;d;};x;p;${x;p;}' file | sed '/^$/d'

Where file is your file.

This is an adapted example from here.

It has a great explanation:

In order to delete the line prior to the pattern,we store every line in a buffer called as hold space. Whenever the pattern matches, we delete the content present in both, the pattern space which contains the current line, the hold space which contains the previous line.

Let me explain this command: x;p; ; This gets executed for every line. x exchanges the content of pattern space with hold space. p prints the pattern space. As a result, every time, the current line goes to hold space, and the previous line comes to pattern space and gets printed. When the pattern /All Present/ matches, we empty(s/.*//) the pattern space, and exchange(x) with the hold space(as a result of which the hold space becomes empty) and delete(d) the pattern space which contains the previous line. And hence, the current and the previous line gets deleted on encountering the pattern Linux. The ${x;p;} is to print the last line which will remain in the hold space if left.

The second part of sed is to remove the empty lines created by the first sed command.

Upvotes: 2

Related Questions