I want to tail a log file and print ONLY blocks of text that match the regex pattern .*\n.*\nABC(.*\n){1-6}XYZ. In case my regex is wrong already, I want to match this example:


Or what I'd really like is to match the above only when the string "ZZZ" doesn't appear anywhere in the match.

My workaround solution is to do the following:

tail -f file | egrep --line-buffered -B 2 -A 6

But obviously this returns much more than I'd like. I also don't have the option to install packages, but I do have awk and sed available.


What you can do is determine the starting and ending pattern. For example, let's use TIMESTAMP HERE and XYZ for that purpose and use sed to find all the lines in betweeen.

sed -n '/^TIMESTAMP HERE/,/^XYZ/p' logfile.txt



tail -f log | sed '/\n/!N;N;/\nABC$/!D;N;N;N;N;N;N;/ZZZ/d'

Displays a line that matches the ABC template, 2 preceding and 6 following if they do not have a template ZZZ

There are always 3 lines in the buffer. We check the last one. If there are no matches, delete the first one and move the execution to the beginning of the script. If there is a match, add 6 more lines. The decision to print is taken from the match according to the ZZZ pattern.

Suppose the XYZ pattern must match the end of the record block. Then:

tail -f | sed ':1;N;/XYZ/!b1;/ZZZ/d'

I don't see the need for unbuffered output here, but -u flag provides this capability in the sed streaming editor.

It is also possible to enter a pattern for the beginning of a recording block:

tail -f | sed '/TIMESTAMP/!d;:1;N;/XYZ/!b1;/ZZZ/d'

The above scripts add to the SED editor buffer(pattern space) all lines from the beginning of the recording to the end and a match is made with the ZZZ template, which results in the determination of deleting the entire record or printing.

Another option, if the log blocks contain the same number of lines, then they can be counted and displayed exactly as much as necessary as in your attempt with egrep.
Let us choose the plus + symbol as the counter. In this example, we will count up to five, but you can change it in this template - +\{5\}:

tail -f log | sed ':1;x;s/$/+/;/+\{5\}/!{x;N;b1};s/.*//;x'

You can also add the beginning of the account and delete by template:

tail -f log | sed '/TIMESTAMP/!d;:1;x;s/$/+/;/+\{5\}/!{x;N;b1};s/.*//;x;/ZZZ/d'

The counter works like this. The SED editor has two buffers - pattern space and hold space. The string is written to the pattern space. The x command changes the contents of both buffers and we write a plus sign at the end of hold space and check their number if the pattern matches 5 plus we print the contents, otherwise we add the next line and increase the counter. After 5 lines match, we reset the counter and the process repeats.

  • Change /\nABC$/ to /ABC[^\n]*$/ for a part of a string.
    – nezabudka
    Sep 14 at 11:17

You could select any block from "TIMESTAMP" to "XYZ" and introduce NUL as separator after the block via sed, then sieve out via other tools, e.g. grep and remove the NUL byte in the end:

tail -f file |
  sed -n '/TIMESTAMP/,/XYZ/{/XYZ/s/$/\x00/;p}' |
  grep -z 'ABC' |
  grep -z -v 'ZZZ' |
  tr -d '\x00'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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