Skip to content

Instantly share code, notes, and snippets.

@lucaspar
Last active November 26, 2022 07:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lucaspar/d6d8e43229ef88f6ffd7ba8f8732cc7b to your computer and use it in GitHub Desktop.
Save lucaspar/d6d8e43229ef88f6ffd7ba8f8732cc7b to your computer and use it in GitHub Desktop.
Bash helper for displaying the last lines of an output continuously without cluttering the entire screen like `tail -f`.
#!/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 "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment