bash prinf \r multiple line show only one line

Viewed 78

I have code :

printf '\r%s' 'first line here' 'second line here' 'thrid line heere'
sleep 1
printf '\r%s' 'iam new' 'new again' 'and new'

but the script show only one line

i want show three line, each line will update after one second, without new line again

only three line and update all three line

thanks

3 Answers

Assuming the objective is to print 3 lines, wait a bit, and then overwrite the same 3 lines, then you'll need to incorporate some additional code that provides for 'up and down' movement of the cursor.

While you can hardcode some of this based on a specific terminal, an easier (most of the time) approach is to use something like tput to manage cursor movement.

In this case you'll need the ability to not only 'go up' with the cursor but also overwrite/clear a previous set of output in the case where the new output is shorter in length; we'll also need to replace the current \r with \n.

The general approach:

EraseToEOL=$(tput el)              # grab code for clearing from cursor to end of line; useful for erasing text from a previous longer print

tput sc                            # save the point we want to return to

printf "%s\n" 'first line here' 'second line here' 'third line here'

sleep 1

tput rc                            # return to our save point (tput sc), ie, the place where the cursor was located just before the 1st printf

printf "%s${EraseToEOL}\n" 'i am new' 'new again' 'and new'

The results:

enter image description here

FWIW, here's what it looks like if we remove ${EraseToEOL} from the 2nd printf:

enter image description here

Carriage returns (\r) ONLY reset to the beginning of the line.
Newline characters (\n) "roll the paper" a line vertically.
Imagine controlling a physical print head.

The result is that you can use \r for dynamic output effects like a countdown.

printf "\n" # get a clean line to start so you don't print over something
for c in {10..1}; do printf "\rNew lines in: %2.2s" $c; sleep 1; done # countdown in place
printf "\r%20.20s\r" ""         # blank the line and reset to overwrite cleanly
printf "New line %2.2s!\n" {1..10}; # include \n's to move to next line, NOT overwrite

This counts down in place, doesn't leave the hanging 0 from the 10 (that's what the %2.2s if for), and writes the first of the lines that stay over where the countdown was without leaving hanging characters there (that's the %20.20s).

edit

I think I understand the OP a little better today.
The main point was to use printf '%s\n' instead of printf '\r%s'.
Others have done an excellent job of demonstrating how to move the cursor back up the screen to a previous point, but I wanted to throw one more perspective.

IF it's ok for this stuff to be the ONLY thing on the screen, there's a simple solution without quite so much explicit vertical cursor management -

clear    # blank the screen, putting the cursor at the top
printf '%s\n' 'first line here' 'second line here' 'third line here'
sleep 1
clear    # blank the screen and put the cursor at the top again
printf '%s\n' 'I am new' 'new again' 'and new'

Sometimes simple is easier to understand, use, and remember.

The other solutions here are a lot more flexible, though. I recommend learning those.

tl;dr :

printf "One Line\nSecond line\nThird line"
sleep 1
printf "\033[1F\033[1Fnew 1\nnew 2\nnew 3"

Not sure what you are trying to do exactly. But I am under the impression that you want to update three different lines.

Eg, you want your output to go from

first line here
second line here
third line heere (sic)

to

iam new
new again
and new

If so, you can't do that with \n, \r... As Paul 's explained, those are inherited from the days when there was no screen but printers. And not modern printers: some electronically controlled good old typing machines. So sending 'A' to the machine, types 'A'. Etc. That the ascii code. And some special codes meant special behaviour. Such as '\r' which return to the beginning of the line (as it is possible with a typing machine, as you know if you've ever seen one). Or '\n', going to next line (using the right handle). Or '\07' ringing the bell. Etc. But you cannot go back 1 line. Well, not with basic control char.

Now, depending on what terminal (the application emulating a physical terminal such as VT100, which itself is a device emulating a paperless printer. The "size of train is derived from size of roman horse ass" joke, is not entirely a myth) you are using, you may use special control sequences.

The most likely to work is ESC [ 1 F one.

So, in bash

printf "One Line\nSecond line\nThird line"
sleep 1
printf "\033[1F\033[1Fnew 1\nnew 2\nnew 3"

You may need to print some spaces to erase the remaining of the former line (or use some other control sequence to do so. You could check vt100 control sequences to find many others)

For example

printf "\n\n" # just to "create" the 2 extra lines
n=1
while true
do
   printf "\033[1F\033[1F"
   printf "n = $n\n"
   printf "n² = $((n*n))\n"
   printf "1000000/n = $((1000000/n))   " # <- note the trailing spaces because this line may become shorter and shorter
   ((n++))
   sleep 1
done

(\033 is ESCape character. So each \033[1F sequence take you to the beginning of the previous line)

It is important tho to understand that you are assuming, doing so, that the terminal you are using understand those sequence. \n and \r are supposed to work with any device that understands ascii control char. VT100 Escape sequence are only working with devices that were designed to be compatible with those sequences. Which is the case of most terminal, but not all. A notable exception is jupyter notebook for example (And probably windows command terminal, but I don't have one to check).

So, if you need a clean solution for such things, you need to find a library such as bashsimplecurses. That will adapt to any terminal.

Related