2

I have file with unknown number of blocks of text consisting of starting keyword "Start", ending keyword "End" and optional text between them with one exact keyword "Disk" on every line and I need to get rid of the ones where there is nothing between them, see the example.

I am processing input like this:

Server1:Start
Server1:End
Server2:Start
Disk1
Disk2
Server2:End
Server3:Start
Disk1
Server3:End

, and my desired output is this:

Server2:Start
Disk1
Disk2
Server2:End
Server3:Start
Disk1
Server3:End

I know, that I can use 'awk' or 'sed' to find text between 2 lines, but I do not know what to do, if there are multiple occurrences of these 2 lines or if there is no text between these 2 lines.

I am running Ubuntu 17.10.

Looking forward to any help.

edit: I deleted the post first time, because I thought that I can do it using sed -e '/Start/,/End/d', but this actually removes everything.

0

2 Answers 2

2

To delete back-to-back Start and End lines, this should do in GNU sed:

$ sed -e '/Start/ {N; /^\(.*\):Start\n\1:End$/d }' < input

if we see Start, load the next line with N, then see if the contents of the buffer are just Somename:Start\nSomename:End with Somename same on both lines (\n is a newline). If so, delete it. Here, \1 is a reference to the first group within \(..\), and matches the same string that was encountered there. .* just means any number (*) of any characters (.).

Using sed -e '/Start/,/End/d' would indeed delete every single line, since the range matches all lines between the starting and ending patterns. Everything in the input is between Start and End, so everything is deleted.

4
  • Actually it isn't literally "Server2" and "Server3". There are actual unknown URLs and the file is much longer than that. Pardon me for not specifying this part.
    – mikro45
    Jan 14, 2018 at 20:44
  • @mikro45, argh, no, pardon me for missing the sentence with the actual point of the question.
    – ilkkachu
    Jan 14, 2018 at 21:01
  • It isn't working for whole file the way, you wrote it. But when I changed it to sed -e '/Start/ {N; /^\(.*\):Start\n\(.*\):End$/d }' < input, changing "\1" for "(.*)", it works now. Can you explain what do "\1" and "(.*)" do?
    – mikro45
    Jan 14, 2018 at 21:33
  • @mikro45, yeah, I assumed the label would repeat identically, as it seemed to be in the example. That was mostly to avoid the possibility that the pattern would match something else, if :End could appear on some other line. /^.*:Start\n.*:End$/ would relax that, and just match any strings before :Start and :End. (Without the \1, the \( and \) are also unnecessary since they only act as a place for the \1 to point to.)
    – ilkkachu
    Jan 14, 2018 at 21:44
1

another solution, as I like trying to do these in awk.

BEGIN { 
    RS="End\n"
    ORS="End\n"
} 
NF > 2

using the built in RS or record separator variable, awk will treat between each End\n as a record, and presuming that the servername:Start and servername:End are both single words, its just a case of printing the lines with more that 2 fields via the NF > 2 line. If this is true, the whole line will be printed, with End\n used as the output record separator (ORS)

~$>echo '
Server1:Start
Server1:End
Server2:Start
Disk1
Disk2
Server2:End
Server3:Start
Disk1
Server3:End
' | awk 'BEGIN { RS="End\n"; ORS="End\n"; } NF > 2;'
Server2:Start
Disk1
Disk2
Server2:End
Server3:Start
Disk1
Server3:End

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .