6

I would like use a program like tail to follow a file as it's being written to, but not display the most recent lines.

For instance, when following a new file, no text will be displayed while the file is less than 30 lines. After more than 30 lines are written to the file, lines will be written to the screen starting at line 1.

So as lines 31-40 are written to the file, lines 1-10 will be written to the screen.

If there is no easy way to do this with tail, maybe a there's a way to write to a new file a prior line from the first file each time the first file is extended by a line, and the tail that new file...

  • 2
    I'm not sure what you mean. When line 31 is written you want line 1 to be printed? So you want a delay? That's not what tail does. – pipe Apr 3 at 14:58
7

Maybe buffer with awk:

tail -n +0 -f some/file | awk '{b[NR] = $0} NR > 30 {print b[NR-30]; delete b[NR-30]} END {for (i = NR - 29; i <= NR; i++) print b[i]}'

The awk code, expanded:

{
    b[NR] = $0 # save the current line in a buffer array
}
NR > 30 { # once we have more than 30 lines
    print b[NR-30]; # print the line from 30 lines ago
    delete b[NR-30]; # and delete it
}
END { # once the pipe closes, print the rest
    for (i = NR - 29; i <= NR; i++)
        print b[i]
}
  • This works, but form the script I would expect it to work like tail, printing out a previous line as each new line is added to the file. Instead it prints out in spurts of ~70 lines after ~100 lines are added to the file. It does not print the most recent 30 lines, so it's pretty close... – ridthyself Apr 3 at 2:25
  • 1
    @ridthyself if you have GNU awk, try adding a fflush(); after the print b[NR-30];. Maybe the output is being buffered. – muru Apr 3 at 2:35
  • 2
    @ridthyself, your awk must be mawk, Try switching to gawk or pass the -W interactive option. – Stéphane Chazelas Apr 3 at 3:31
  • 1
    Once I switched to gawk, it worked perfectly. Thank you! Using tmux to split my wide screen into two columns, I can log the terminal to a file using script, then use this code in the other window to create an console overflow window -- works like a dream. – ridthyself Apr 4 at 22:02
8

Same as @muru's but using the modulo operator instead of storing and deleting:

tail -fn+1 some/file | awk -v n=30 '
   NR > n {print s[NR % n]}
   {s[NR % n] = $0}
   END{for (i = NR - n + 1; i <= NR; i++) print s[i % n]}'
  • 1
    Does this keep every line in s until awk finishes? – l0b0 Apr 3 at 18:31
  • 2
    @l0b0, that keeps n lines in s, NR%n has values ranging from 0 to n-1 – Stéphane Chazelas Apr 3 at 19:05
  • 1
    Ah, of course, saw the modulo now. – l0b0 Apr 3 at 19:36
3

This isn't very efficient, because it will re-read the file two seconds after reading it last time, and you will miss lines if the output is coming too fast, but will otherwise do the job:

watch 'tail -n40 /path/to/file | head -n10'
  • 1
    What would thi behaviour of this look like if the file takes more than 2 seconds to read? – Darren H Apr 3 at 6:10
  • 1
    @DarrenH With watch --precise, I'm not sure, but I would guess it runs the command back-to-back. With plain watch, it should run the tail/head pipe, wait two seconds, run it again, wait another two seconds, and so on. – a CVn Apr 3 at 14:54
  • 1
    Will this meet OP's requirements if more than 30 lines are added to the file per watch interval? – a CVn Apr 3 at 14:56

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.