Overwrite multiple lines of stdout at the command-line without losing terminal scrollback

scrollingterminal

I'd like to create multiple progress bars and update them on the go, without the need to clear the whole screen (I'd like the previous stdout contents to remain on the screen).

I need to:

  1. Write a few lines with echo
  2. Wait with sleep
  3. Get my cursor back to the start of the first line
  4. Overwrite my few lines with updated information
  5. Repeat from step 2.

The think is I'd like the user to be able to scroll back to read the previous stdout contents.

If I didn't care about the scrollback, I'd just use clear and write out again what I need:

while [ true ]; do echo "$SECONDS"; sleep 0.1; clear; done

This however makes the scrollback completely unreadable, because clear simply prints out enough newlines to push previous terminal output out of view.

When I run htop and close it – it restores my terminal to the exact same state where it started htop. Can something like this be achieved in a bash script?

Best Answer

Use tput sc tput ed and tput rc.

From tput manual:

tput sc

Save the cursor position

tput rc

Restore the cursor position

tput ed

Clear to end of screen

Example:

tput sc
while :; do
    tput ed
    echo -e "$SECONDS\n$SECONDS\n$SECONDS"
    sleep 1
    tput rc
done

How does it work?

  1. Stores cursor position with tput sc
  2. Enters the loop that'll update information on screen
  3. tput ed clears to the end of screen to assure proper result
  4. Then then the multiple lines of changing data are written with echo
  5. sleep 1 wait one second before refreshing
  6. tput rc moves the cursor back to the beginning of the lines that we just printed
  7. loop repeats overwriting with new information
Related Question