-1

In a contrived example, if I have the following:

sup="$(printf "\e[s" > /dev/tty; printf "one" > /dev/tty; printf "\e[u\e[Jtwo" > /dev/tty)"

The output will successfully erase one leaving only:

two

However, if I use echo "one" to print a newline with it:

sup="$(printf "\e[s" > /dev/tty; echo "one" > /dev/tty; printf "\e[u\e[Jtwo" > /dev/tty)"

Then the output is:

one
two

Why would the newline break the cursor handling? And how could I work around it?

A more comprehensive example would be:

sup="$(printf "\e[s" > /dev/tty; for (( i=0; i<5; i++)); do echo -e "a random number is:\n$RANDOM" > /dev/tty; sleep 1; printf "\e[u\e[J" > /dev/tty; done; echo 'result')"
echo "sup=$sup" # sup=result
Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
balupton
  • 47,113
  • 32
  • 131
  • 182
  • Why are you assigning the output to a variable? Since everything is redirected to /dev/tty, the variable will get an empty string. – Barmar Sep 10 '21 at 15:20
  • The examples are contrived to simplify a requirement of newlines, command substitution, and TTY erasure. The actual use case is assigning the stdout result of a command that asks the user a series of questions and erases the questions once completed. [1](https://github.com/bevry/dorothy/blob/e5eb65dae2129703e7f336df7f9ae34b4820218a/commands/ask), [2](https://github.com/bevry/dorothy/blob/e5eb65dae2129703e7f336df7f9ae34b4820218a/commands/choose-menu), the real commands currently use `clear` which erases everything. – balupton Sep 10 '21 at 15:35

2 Answers2

1

I suspect that you're writing to the last line of the window. Writing a newline will cause the window contents to scroll. When you restore the cursor position with ESC [ u, it returns to the physical position in the window that was saved with ESC [ s, not the position in the scroll buffer. But the word one will have scrolled up one line, so two will be written to the line after it rather than overwriting it.

Barmar
  • 741,623
  • 53
  • 500
  • 612
0

As @Barmer's answer alluded to, the reason why restore cursor does not work, is that saving the cursor only saves the left/right margins, not the row:

Saves the cursor position/state in SCO console mode.[22] In vertical split screen mode, instead used to set (as CSI n ; n s) or reset left and right margins.[23] https://en.wikipedia.org/wiki/ANSI_escape_code#CUP

I was able to come up with a portable solution that does not need me to count line numbers.

tty.bash

#!/usr/bin/env bash

# sourced from:
# https://stackoverflow.com/a/69138082/130638
# inspired by:
# https://unix.stackexchange.com/a/88304/50703
# https://stackoverflow.com/a/2575525/130638
# https://askubuntu.com/a/366158/22776
# https://stackoverflow.com/a/5810220/130638
get_tty_snapshot () {
    local pos oldstty y x y_offset="${1:-0}" x_offset="${2:-0}"
    exec < /dev/tty
    oldstty=$(stty -g)
    stty raw -echo min 0
    echo -en "\e[6n" > /dev/tty
    IFS=';' read -r -d R -a pos
    stty "$oldstty"
    y="$((${pos[0]:2} - 2 + y_offset))"
    x="$((pos[1] - 1 + x_offset))"
    echo -en "\e[${y};${x}H\e[J"
}
use_tty_snapshot () {
    echo -en "$1" > /dev/tty
}

embed.bash

#!/usr/bin/env bash
source "./tty.bash"

tty_snapshot="$(get_tty_snapshot)"

for (( i=0; i<5; i++)); do
    echo -e "one random number $RANDOM\nanother random number: $RANDOM" > /dev/tty
    sleep 1
    use_tty_snapshot "$tty_snapshot"
done

echo 'result'

example.bash

#!/usr/bin/env bash

echo "ask result"
result="$(./test-cursor-embed)"
echo "got result=[$result]"
balupton
  • 47,113
  • 32
  • 131
  • 182