146

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,677
  • 4
  • 27
  • 35
Marc-Olivier Titeux
  • 1,593
  • 2
  • 10
  • 8

6 Answers6

183

It might suffice to use watch:

$ watch tail -n 15 mylogfile.txt
William Pursell
  • 3,497
  • 1
  • 16
  • 19
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
  • 51,578
  • 10
  • 158
  • 140
Turgon
  • 451
  • 3
  • 4
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.

Niklas Rosencrantz
  • 4,112
  • 6
  • 36
  • 58
  • 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 "$@"

TheMechanic
  • 171
  • 7