Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I have a file whose content is as follows:

alfa
[many lines here]
TAG1
TAG2

bravo
TAG3

charlie
TAG4
[many lines here]

where TAG1, TAG2, TAG3 and TAG4 are fixed strings and alfa, bravo and charlie change time to time, and I want to extract:

alfa-bravo-charlie

What is the precise sed command I have to use? I do not know how to work with multi-line pattern. :(

P.S.: I'm using sed for windows.

share|improve this question

2 Answers 2

up vote 3 down vote accepted

This works with gnu sed, I don't think it relies on any gnu-specific extensions but I don't know.

echo "$yourdata" | sed -ne '1{h;d}; /^TAG1$/ {n; /^TAG2$/{n;N;N; /\nTAG3$/ {s///; H; n;N;N; /\nTAG4$/ {s///; H; g; s/\n\n/-/gp; q; } } } }'

Result: alfa-bravo-charlie

How does it work? First we tell sed "-n" we want not to print anything unless we specifically say [p]rint.

The first block of the sed expression is "1{h;d}". This says when we read line 1, stash that line in the [h]old buffer then [d]elete it from the working buffer so that we'll read the next line and pass it through the sed expression from the start.

When reading subsequent lines the "1{...}" block will be skipped.

We don't match anything further until we hit the line TAG1. At this point we execute the long {...} block. This says first read the [n]ext line, overwriting the TAG1 line which was in the buffer. If the buffer now is TAG2, then we execute the next inner {...} block. That first reads the [n]ext line, overwriting what's already in the buffer. The next two commands are "N;N". This means read the next 2 lines but append them to the work buffer, rather than overwriting it. If the work buffer now matches /\nTAG3$/, then we execute the next inner {...} block. That says first "s///", in other words substitute the empty string for the most-recently matched expression. This deletes the "\nTAG3" from the end of the working buffer, leaving "\nbravo". Then we do [H], which appends that to the hold buffer. ([h] overwrites the hold buffer, [H] appends to it). So now the hold buffer contains the first line "alfa", then the next line "\nbravo". These are joined by a newline, so we've really got "alfa\n\nbravo." We'll take care of the two newlines later.

We keep going until we've got "alfa\n\nbravo\n\ncharly" in the hold buffer. Then we say [g]et the hold buffer (overwriting whatever is in the working buffer). We do a "s/\n\n/-/" on this to turn the double newlines into dashes. We add "g" and "p" flags to the end of the [s] command so that the substitution works globally (i.e. doesn't just do one substitution then stop) and that the result after substitution is [p]rinted.

Then we [q]uit, we don't need to read the rest of the input stream.

share|improve this answer

It's not clear from your example exactly what you're trying to do. It sounds like you're trying to discard the entire contents of the file other than a set of three markers, which you want to join together. You don't need sed for this, you can just type:

echo alfa-bravo-charlie

And you've accomplished your goal. If you simply want to remove the content between "alfa" and "charlie", you could use a sed script like this:

/charlie/ a\
alfa-bravo-charlie
/alfa/,/charlie/ d

If this isn't what you want to do, it might help if you were to clarify your example.

share|improve this answer
    
Thank you, I think now my question is better explained. –  Toc Dec 14 '09 at 15:43

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.