Using sed:
#!/bin/sed -nf
/HD loop$/ {
x
G
N
p
s/.*\n\([^\n]*\)/\1/
}
h
When "HD loop" is found at the end of a line (indicated by the $
character), a command block is executed. This command block starts by swapping the contents of the hold space (an auxiliary buffer) with the contents of the pattern space (the working buffer), using the x
exchange command. As we will see later, we will keep the hold space with the last line read. The G
command will append the contents of the hold space (which now contains the current line) into the pattern space, and the N
command will read the next line of the input and append it into the pattern space. We can then print the pattern space with the p
command. The last thing to do is to restore the hold space. We do this using two commands. The first is a substitute command that removes all lines except the last one from pattern space. Then we copy the pattern space to hold space with the h
command.
Even if the line doesn't match "HD loop", it is copied to hold space. By doing this, the hold space will always contain the contents of the previous line. Beware that because of the way we set up the hold space after a match is found, it doesn't properly recognize two matches that appear on successive lines. If you want to consider this, some special treatment is needed:
#!/bin/sed -nf
/HD loop$/ b next
h
:start
n
/HD loop$/ {
x
G
:next
N
p
s/.*\n\([^\n]*\)/\1/
/HD loop$/ b next
d
}
h
b start
For a more complete and general version, we must first consider what happens when the "HD loop" is found on the first line. In the previous version, it would print an empty line followed by the "HD loop" line. Because this can confuse the output into thinking that HD loop was actually preceded by an empty line we must use special treatment for this. The special treatment is to override sed's evaluation loop, using our own.
We define a start
label with the :
command, which defines the start of our loop. Then, at the end of the script, we use the b
branch command to jump back to the start of the loop. To fully mimic sed's evaluation loop, the first command after the start
label is the n
next command, to read the next input line into the pattern space.
With our loop defined, we can treat the first special case, which is when the first line starts with HD loop. If it does, we have to skip loading the contents of the hold space, because we know it doesn't contain any useful data. Let's define a label next
right after the G
command to append the contents of the hold space. We can now use /HD loop/ b next
to skip the hold space manipulation and just print the current line and the line that follows.
If the first line doesn't start with "HD loop", we must store it into hold space before n
replaces it with another command. So we do that with the h
command.
The next special case is when two "HD loop" lines appear following each other. In that case, at the end of the block in the previous version, we can check if the newly read line contains "HD loop", and if it does, we can simply jump back to the the next
label in order to read another line and print it. We can do this as many times necessary, treating as many consecutive "HD loop" lines available.
The last special case is when two "HD loop" lines appear separated by a single line. If we leave it the way it is, this case will print the line between the "HD loop" lines twice. To treat this, we must act as if the hold space doesn't need to be printed if a "HD loop" line is found right after a match. Because this situation is analogous to what happens when we are looking into the first line of the input, we can use the d
delete command right at the end of the match to clear the pattern space and restart the whole script. Now it behaves as if the line was the first input line, and won't print the hold space if the line after the match is an "HD loop" line.
UPDATE: If you only want the first result, you can simplify a few things:
#!/bin/sed -nf
/HD loop$/ b next
h
:start
n
/HD loop$/ {
x
G
:next
N
p
q
}
h
b start
Now, instead of performing all of the previous operations after printing the line, we can just quit with the q
command.
Hope this helps =)