You are not logged in.

#1 2022-03-16 14:19:27

Ferdinand
Member
From: Norway
Registered: 2020-01-02
Posts: 331

Bash cursor position control

I am trying to store the cursor position in Bash using an additional method to the normal escape sequences \e7 and \e8.

The reasoning is that I need to first store my cursor position in order to jump back to its location later, but when I later do jump, I need to store the position I am jumping from, so that I can return there again afterwards. I need to store two cursor locations, in other words.

Now, I found a way to do that second storing with a function:

savepos()
{
    # Save TTY settings
    exec < /dev/tty
    savedtty=$(stty -g)
    # Mute TTY
    stty raw -echo min 0
    # Catch current cursor position
    echo -en "\e[6n" > /dev/tty
    IFS=';' read -r -d R -a rawpos
    # Restore TTY settings
    stty "$savedtty"
    # Extract row and column of the cursor position
    eval "posrow=$((${rawpos[0]:2} - 2))"
    eval "poscol=$((${rawpos[1]} - 1))"
} 

I understand more or less what I do here, but obviously not completely so, because can't understand why this function only works if I call it straight.
I.e. if I call it through something like echo $(savepos), it executes, but it won't store the cursor position:

$ savepos;printf "Position: $posrow $poscol\n"
Position: 0 0
$ savepos;printf "Position: $posrow $poscol\n"
Position: 2 0
$ printf "$(savepos)Position: $posrow $poscol\n"
Position: 2 0

Any insights will be much appreciated smile

Offline

#2 2022-03-16 14:45:58

seth
Member
Registered: 2012-09-03
Posts: 49,992

Re: Bash cursor position control

Store where?
$(savepos) executes savepos in a subshell - assignments there are not relevant to the parenting shell

echo $foo
 echo $(foo=bar)
echo $foo
echo $(foo=bar; echo $foo)
echo $foo

Offline

#3 2022-03-16 17:55:56

Ferdinand
Member
From: Norway
Registered: 2020-01-02
Posts: 331

Re: Bash cursor position control

Aw.. of course - that wouldn't work then smile
I didn't really think of

printf "$(command)"

as running command in a subshell - to me it was just an alternative to using backticks - but evidently backticks too run in a subshell :-p

What I'm trying to do is just sillybilly functionality-wise, but I got a bit pig-headed when it proved more difficult than I initially thought:

I'm trying to change the looks of the command I typed at the prompt after I have entered the command. So, say, while I type the command it is normal text, but after I press enter, before the command executes, I want it bold. As part of a fancy-prompt.thing smile

What I (try to) do is I have a fairly normal PS1 and do the change from PS0:

PS1="\e[0;37m\! \e[0;32m\w\e[1;33m:\e[0m\e7 "
PS0="$(savepos)\e8\e[1m\e[$posrow;0H\e[0m"

Basically my prompt shows me my history number, current path and a colon, after which I store the cursor position.
Then my PS0 stores the current cursor position (which may be anywhere with multiline commands), jumps to my PS1 stored cursor position, inserts a bold escape sequence, jumps back to the last stored position (right at the end of the entered command), inserts the escape sequence for normal text - and then the command executes.

But I'm setting the position variable in a subshell, so it won't stick.
And I guess I'm rather stuck with this one, as a subshell can't easily set a variable in the calling shell (in a normal script I could write to a file in the subshell and read it back in the calling shell or that sort of mumbo jumbo, but this is a prompt definition)?

Last edited by Ferdinand (2022-03-16 17:57:30)

Offline

#4 2022-03-17 07:21:30

seth
Member
Registered: 2012-09-03
Posts: 49,992

Offline

#5 2022-03-17 08:41:30

Ferdinand
Member
From: Norway
Registered: 2020-01-02
Posts: 331

Re: Bash cursor position control

Thanks, that's a nice try, but \e7 and \e8 are the same as \e[s and \e[u smile

Using

PS1="\e[0;37m\! \e[0;32m\w\e[1;33m:\e[0m\e7 "
PS0="X\e[s\e8Y\e[u\e[1mZ"

I get

1 ~: printf "\n\n"
XZ

2 ~: 

where the Z is bold, meaning it writes an "X", stores the position, restores the same position, writes an Y, restores the same position again, overwrites Y with a bold escape sequence and then a Z.

Using

PS1="\e[0;37m\! \e[0;32m\w\e[1;33m:\e[0m\e7 "
PS0="\e8\e[1mX"

I get

1 ~:Xprintf "\n\n"

2 ~: 

where the X is bold, meaning it restores the position from PS1, writes bold escape sequence and then a X.

Crucially, this means writing the escape sequence in front of the old text does not make it bold, the only bold thing in that last example is the X, and that got written after the escape sequence.

This I should have checked in the first place, of course. It seems Ferdinand is rather more like a dog than a bull; following my nose without thinking tongue

Last edited by Ferdinand (2022-03-17 08:42:07)

Offline

#6 2022-03-17 12:58:00

seth
Member
Registered: 2012-09-03
Posts: 49,992

Re: Bash cursor position control

\e7 and \e8 are the same as \e[s and \e[u

No. See the note in the link I posted.

this means writing the escape sequence in front of the old text does not make it bold

Of course not tongue

This I should have checked in the first place, of course.

Yes.
You'll have to move to the line and replace the text. This is certainly possible w/ zle (zsh) where you can access the $BUFFER - you should™ be able to access the last command as "!$", but whether it's ultimately possible to get this down in a function triggered by $PS0 like zsh's preexec() function idk. - I guess you'll figure smile

Offline

#7 2022-03-17 14:35:27

Ferdinand
Member
From: Norway
Registered: 2020-01-02
Posts: 331

Re: Bash cursor position control

Cursor Controls wrote:

Note: Some sequences, like saving and restoring cursors, are private sequences and are not standardized. While some terminal emulators (i.e. xterm and derived) support both SCO and DEC sequences, they are likely to have different functionality. It is therefore recommended to use DEC sequences.

That's the one you mean?
ESC 7 and ESC 8 are DEC, while ESC[s and ESC[u are SCO, and they could have worked differently, but evidently, in my xfce4-terminal, they act the same.
At least that's how it looks to me, based on the example below, where I use

PS1="\e[0;37m\! \e[0;32m\w\e[1;33m:\e[0m\e7 "
PS0="X\e[s\e8Y\e[u\e[1mZ"
Seth wrote:

Of course not tongue

Hehe, well it seems obvious now, but to my thinking the text in the terminal is stored in the terminal buffer, and is presented to the user one screenful at the time. The text may contain formatting codes that works on the text following the code, so if I modify that text by inserting a formatting code, then the text following that code should be modified according to the formatting (like text in a word processor), but I guess that's not quite how it works :-p

Seth wrote:

You'll have to move to the line and replace the text.

And, sadly, that doesn't seem possible in Bash. I can't use !! or !$ in PS0 as it doesn't expand them; theyll just be text.
I can, hower, use $BASH_COMMAND, but that variable is not assigned the command entered at PS1 by the time PS0 is displayed, so it's always one command behind. And also, the command it shows is the assignment to PS0, so it'll look like this:

PS0="$BASH_COMMAND\n"

$ echo test
PS0="the last command from .bashrc
"
test
$ echo test2
PS0="echo test
"
test2
$

So that's it then :-/
Thank's for helping out!

Offline

Board footer

Powered by FluxBB