My script doing something like:

while :;
   clear

   do_a_lot_of_output_here

   sleep 1
done

Is it any options to prevent screen of blinking when I'm doing clear and output? I want to do it like in watch command (but it's written in C). Any advices?

clear | hexdump -C

00000000  1b 5b 48 1b 5b 32 4a                              |.[H.[2J|
00000007

PS. I'm using bash only.

up vote 7 down vote accepted

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:

  • For those who use #!/bin/bash and wish to use watchit embedded in a small stand-alone script with some functions, you can do export -f function_name; watchit function_name. – aggregate1166877 Jun 21 '16 at 5:11
  • This worked perfectly for me except that I ran into funny issues at the top of the screen, where things would shift and get glitchy for longer chunks of text (anything getting shortened by head). Seems the issue is an off-by-one error (at least in my setup -- ssh session in split panes, iTerm2) when computing the number of rows. ROWS=`expr $(tput lines) - 1` addressed this perfectly. – ohruunuruus Sep 9 '16 at 23:54
  • This is wonderful, thank you very much @thomas-dickey. – mbarkhau Sep 3 at 9:12

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.

  • Double buffering, just like in any other situation, safes the day :-) – Ikke Sep 29 '15 at 19:49
  • Worked like a charm. No flickering at all :) – Lars Juel Jensen Feb 24 '17 at 10:00
  • 1
    The remaining flickering can be eliminated by including the clear into the buffer, i.e. output=$(clear; do_a_lot_of_output_here). – kdb Jul 20 '17 at 12:15
  • @kdb Nice suggestion! This almost completely eliminates flickering for me and is much simpler than the accepted answer. – Michael Mior Jun 5 at 15:00

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.

  • In this case cursor starts jumping to the end of output from random position (and artifacts of course). Also I tried tput clear with the same results (blinking) – ravnur Jun 29 '13 at 21:31
  • I added a couple of lines to the script to hide the cursor. – Smith John Jun 29 '13 at 21:40
  • No cursor - no jumping. But about artifacts? Output has a random amount of rows. Do I need to fill all script with spaces before starting new output? But, anyway, thanks for your time and efforts: +1 from me for that but it doesn't solve my issue – ravnur Jun 29 '13 at 22:08
  • I found only one way to get rid of all artifacts: tput ed. But it cause blinking – ravnur Jun 29 '13 at 22:33
  • What terminal emulator are you using? With tput ed right before the do_a_lot... line, I'm seeing blinking in urxvt but not xterm or st. – Smith John Jun 30 '13 at 0:18

As an extension of Cristian's answer, I made the following bash function which works if the console window is smaller than the output of the command:

function watcher()
{
    lines=$(tput lines)
    while true; do
        output="$($@ | head -n $lines)"
        clear
        echo -e "$output"
        sleep 2
    done
}

This allows you to pass any command to the watcher. If you are using git, use git config --global color.status always and then:

watcher git status

Will display an always-updating git status output.

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.