8

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?

2 Answers 2

6

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
1
  • 1
    Unfortunately tput cursor and dimensions operations seems to be unreliable. Some times they all work great, and sometimes tput rc just doesn't work. Sometimes a sub-function called in Bash recieves wrong cols and rows information.
    – unfa
    Commented Apr 24, 2017 at 7:58
1

You could use tput cuu1 to move the cursor up a line (or however many lines you need to move up to overwrite) and then overwrite.

For example:

echo -e "foo\nbar"
tput cuu1
tput cuu1
echo -e "baz\nquux"

yields:

baz
quux

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .