'How to overwrite a multiline string in bash?

I have this code snippet, which allows to overwrite a string in bash if used multiple times:

echo -ne "String 1  \r"
echo -ne "String 2  \r"

Output:

String 2

However, the string is not overwritten if the string is wider than the terminal window:

echo -ne "Very very long string without any embedded newline charactes that is longer than the window width  \r"
echo -ne "String 2  \r"

Output:

Very very long string without any embedded newline charactes that is longer than the window width
String 2

How can I force overwrite for the entire string, independent of the terminal window's size?

Helpful answers do not:

  • assume that I do not write until the bottom of the terminal window (see above requirement regarding independence of window size)
  • assume that clear may be used in this context


Solution 1:[1]

Assumptions:

  • code generates output that may display on more than one line in the console/window (either multi-line variable w/ embedded carriage returns or just a really long line that causes a wrap when it hits the 'end' of the console/window)
  • output does not cause the terminal to 'scroll up' (eg, program does not start printing output in the last line of the console/window
  • do not know how many console/windows lines will contain the output string

First thing we'll do is provide a string that's actually longer than my console/window.

# 'stty -a' shows my terminal width ('columns') is 87, so 200 should be a long-enough string to play with

$ printf -v longstring '%0.sx' {1..200}
$ echo "${#longstring}:${longstring}"
200:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

To simulate 'backing up to front of line' we can use tput, eg:

erase=$(tput ed)                  # save control codes for 'clear to end of line'
clear                             # clear console/window to insure we're not generating output at/near bottom of console/window

printf "\n\n################ begin output"

tput sc                           # save current cursor position

for i in {1..5}
do
    printf -v count "%0s" $i
    echo -en "${count} ${longstring} EOS\r"
    tput rc                       # move cursor back to our save point
    sleep 1

    printf "${count} short string${erase}"
    tput rc
    sleep 1
done

# pick enough `\n` to get us 'past' our lines of output

printf "\n\n\n################ end output"

On the first pass through the loop we see:

########### start test
01 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx EOS

And one second later we see:

########### start test
01 short string

On subsequent passes through the loop the output line is overwritten with 02 xxx..., 02 short string, 03 xxx..., 03 short string, 04 xxx..., 04 short string, 05 xxx... and then finally:

########### start test
05 short string


########### end test

Solution 2:[2]

echo 'Very very long string without any embedded newline charactes that is longer than the window width'

echo -e '\e[1A\e[KString 2'

String 2

return to the previous line using \e[1A and clear that line using \e[K

Test

for((i=2000; i>=0; i -=25))
do
  sleep 0.01
  echo -e "\e[1A\e[KString $i"
done

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2