148

I have a growing log file for which I want to display only the last 15 lines. Here is what I know I can do:

tail -n 15 -F mylogfile.txt

As the log file is filled, tail appends the last lines to the display.

I am looking for a solution that only displays the last 15 lines and get rid of the lines before the last 15 after it has been updated. Would you have an idea?

рüффп
  • 1,707

6 Answers6

185

It might suffice to use watch:

$ watch tail -n 15 mylogfile.txt
34

If you use watch, try the -n option to control the interval between each update.

Thus, the following would call tail every 2 seconds

$ watch -n 2 tail -n 15 mylogfile.txt

while this one polls it every 1 second

$ watch -n 1 tail -n 15 mylogfile.txt
Mat
  • 52,586
Turgon
  • 451
16

You could stream the logfile running less and pressing SHIFT + F that will stream the file using less. $ less mylogfile.txt Then just press SHIFT + F and it will stream. I think it is convenient for monitoring log files that update.

  • 1
    That's a very nice one! – kaiser Mar 10 '16 at 15:21
  • Indeed, not what I was looking for but close. You should also be able to use less +F mylogfile.txt as the manual says it it is similar in behavior to tail -f. If you, dear comment reader haven't used less +G mylogfile.txt yet, then you now know how to immediately jump to the end of a log file. A useful and great time saver on old an poorly maintained systems. – LiveWireBT Feb 06 '23 at 10:14
8

Maybe you find the -d param handy.

man watch

-d Highlight the differences between successive updates. Option will read optional argument that changes highlight to be permanent, allowing to see what has changed at least once since first iteration.

michalzuber
  • 211
  • 2
  • 6
0

In Solaris, AIX or HPUX or UNIX-like (including Linux) you can use scripts to monitoring logs or anything like that:

while true; 
    clear; 
    do date; 
    echo ;
    echo "MONITORING LOG IN "/path/to/file.log": "; 
    echo "Obs.: Last 20 lines of a logfile:
    echo ;
    tail -20 /path/to/file.log;
    echo ;
sleep 5; 
done
0

Old question, but I've decided to write myself a bash function that does exactly that. Pasting the script here for those who want it. "ntail" preserves the last N lines and has a timeout before updating the screen to minimize flickering effects when stdout updates too often.

You can try it out with the following example command, for which the screen updates should preserve the "date", but render a scrolling effect on the stdout of the for loop:

date; for i in $(seq 1 2000); do echo $i; sleep 0.03; done | ntail 10
#!/bin/bash
# Display last N lines of input like tail, but cleaning the screen before every update.
# Example: date; for i in $(seq 1 2000); do echo $i; sleep 0.03; done | ntail 10

function ntail {

# default to 10 lines of tail output
NUM_LINES=${1:-10}

# gets the current time in milliseconds
function mstime() {
    date +%s%3N
}

LAST_UPDATE=$(mstime)   # last time the screen was updated
NEEDS_REFRESH=false     # whether to refresh the screen
SCREEN_BUFFER_SIZE=0    # number of lines on the screen
while IFS= read -r NEW_LINE; do

    # concatenate new the new line to the buffer
    TAIL_BUFFER="$TAIL_BUFFER$NEW_LINE"$'\n'

    # if last update is greater than 100ms, refresh screen
    if [ $(($(mstime) - LAST_UPDATE)) -gt 100 ]; then
        NEEDS_REFRESH=true
    fi

    # refresh screen if needed
    if [ "$NEEDS_REFRESH" = true ]; then

        # reduce buffer size to last NUM_LINES lines
        TAIL_BUFFER=$(echo "$TAIL_BUFFER" | tail -n "$NUM_LINES")$'\n'

        # clear the last SCREEN_BUFFER_SIZE lines, preserving the stdout above that
        for _ in $(seq 1 "$SCREEN_BUFFER_SIZE"); do
            printf "\033[1A\033[2K"
        done

        # print the new buffer
        printf "%s" "$TAIL_BUFFER"

        SCREEN_BUFFER_SIZE=$(echo "$TAIL_BUFFER" | wc -l)
        SCREEN_BUFFER_SIZE=$((SCREEN_BUFFER_SIZE - 1))
        LAST_UPDATE=$(mstime)
        NEEDS_REFRESH=false

    fi

done < /dev/stdin

}

ntail "$@"