-3

While creating a countdown timer in , I came up with the following code:

for ((n=15; n > 0; n--)); do
    printf "Reload |  $n" && sleep 1
done

This works fine, it keeps adding the printf to the same line, as expected.


So I opened the documentation on tput and found:

tput el1

Clear to beginning of line

And tried it out:

for ((n=15; n > 0; n--)); do
    tput el1; printf "Reload |  $n" && sleep 1
done

However, this adds a tab on each iteration, so the output becomes:

Reload | 15
            Reload | 14
                        Reload | 13

Those outputs are on the same line, so the 'clear' works but for some reason is the cursor not restored to the first column.


I've managed to fix it by adding a carriage return (\r) behind the printf:

for ((n=15; n > 0; n--)); do
    tput el1; printf "Reload | $n\r" && sleep 1
done

I've read quite some docs, but can't grasp why the \r is needed here. Please point me to the right documentation/duplicate about this matter.

0stone0
  • 34,288
  • 4
  • 39
  • 64
  • there's no need for `tput`, a simple `printf "Reload | $n\r"` will work` because `\r` is the carriage return – phuclv May 19 '22 at 15:00
  • Just using `printf "Reload\t| $n\r"` will show an `0` before `$n` when going below `10` (like `90`, `80` ...). So that's why I went looking for `tput`. – 0stone0 May 19 '22 at 15:03
  • of course you also need to overwrite the string manually if using only `\r` – phuclv May 19 '22 at 15:04
  • Yea I understand, but looking at `tput`, that should do both? Just curious why the `\r` is needed with `tput el1` in this case. – 0stone0 May 19 '22 at 15:06
  • 1
    "Clear to the beginning of the line" doesn't imply cursor movement. A carriage return does. Compare this to the documentation for `clear`, which explicitly says (emphasis added) "clear screen **and** home cursor". – chepner May 19 '22 at 15:28

1 Answers1

3

Compare el1

tput el1
    Clear to beginning of line

and clear

tput clear
    clear screen and home cursor

Note that clear explicitly states that it moves the cursor; el1 does not. It only erases whatever was on the current line between the start of the line and the current cursor position, leaving the cursor where it is for the following text.

The carriage return, on the other hand, is typically interpreted as moving the cursor to the beginning of the current line without advancing to the next line. The corresponding terminal capability would be cr.

A more robust solution would be to move the cursor first, then clear to the end of the line, and finally output your next bit of text. This handles the case where a new bit of text is shorter than the previous, which will be an issue when you switch from double-digit to single-digit numbers.

for ((n=15; n>0; n--)); do
  tput cr el; printf "Reload |  %2d" "$n"; sleep 1
done

(The %2d is to ensure the single-digit values don't "jump" one space to the left.)

chepner
  • 497,756
  • 71
  • 530
  • 681