Prevent text/screen blinking when doing clear

One way to help prevent flashing is to get all the output before clearing the screen so that there is a minimum amount of time between clearing and redrawing it. This is similar to the concept of double buffering:

while :; do
   output=$(do_a_lot_of_output_here)
   clear
   echo "$output"
   sleep 1
done

This does not completely eliminate the flicker, but it happens significantly less frequently in my experience.


The flashing occurs because the script clears the entire screen. If it paints over the existing text and clears only as necessary, then there will be no flickering.

Here is an example:

#!/bin/sh
watchit() {
    HOME=$(tput cup 0 0)
    ED=$(tput ed)
    EL=$(tput el)
    ROWS=$(tput lines)
    COLS=$(tput cols)
    printf '%s%s' "$HOME" "$ED"
    while true
    do
        CMD="$@"
        ${SHELL:=sh} -c "$CMD" | head -n $ROWS | while IFS= read LINE; do
            printf '%-*.*s%s\n' $COLS $COLS "$LINE" "$EL"
        done
        printf '%s%s' "$ED" "$HOME"
        sleep 1
    done
}

watchit top -b -n 1

It does this:

  • prints the output from the given command that will fit on the screen (no wrapping or scrolling)
  • writes over existing lines, clearing the portion of each line which is not overwritten
  • uses the ed capability of your terminal to print from the current location to the end of the screen.

If you wanted to handle a resizable screen, you could move the assignments to ROWS and COLS inside the outer loop, e.g.,

#!/bin/sh
watchit() {
    HOME=$(tput cup 0 0)
    ED=$(tput ed)
    EL=$(tput el)
    printf '%s%s' "$HOME" "$ED"
    while true
    do
        ROWS=$(tput lines)
        COLS=$(tput cols)
        CMD="$@"
        ${SHELL:=sh} -c "$CMD" | head -n $ROWS | while IFS= read LINE; do
            printf '%-*.*s%s\n' $COLS $COLS "$LINE" "$EL"
        done
        printf '%s%s' "$ED" "$HOME"
        sleep 1
    done
}

watchit top -b -n 1

because tput asks for the current screensize from the system.

Further reading:

  • terminfo - terminal capability data base
  • tput, reset - initialize a terminal or query terminfo database

The flashing is an unavoidable result of clearing the screen each time around the loop. You can move the cursor to the top of the screen and overwrite parts of your old output instead.

# You may want to do this if your code is in a script.
unhide_cursor() {
    printf '\e[?25h'
}
trap unhide_cursor EXIT

# Hide the cursor (there is probably a much better way to do this)
printf '\e[?25l'
clear 
while true ; do
    # Move the cursor to the top of the screen but don't clear the screen
    printf '\033[;H' 
    do_a_lot_of_output_here
    sleep 1
done

This script will leave artifacts if your output shrinks. It's also not very likely to be portable. I've only tested it with urxvt, xterm, and st.

Tags:

Bash