Obter posição vertical do cursor

10

Isso pode parecer muito estranho, mas eu sei como definir a posição vertical do cursor no Bash assim:

echo -e "\e[12H"

Isso move o cursor para a 12ª linha (começando com 1).

Então, como obtenho a posição do cursor (número da linha) usando o linux bash? Seria útil se eu pudesse simplesmente armazenar esse valor em uma variável para poder calcular com ele.

EDITAR:

Este é o erro que recebo:

$ sh rowcol.sh
-en
    read: 9: Illegal option -d
                              test.sh: 12: Bad substitution
BrainStone
fonte
Veja também um exemplo de script (não o mais simples, porque esse tinha restrições adicionais).
Gilles 'SO- stop be evil' -

Respostas:

5

Consegui usar alguns dos exemplos do mesmo artigo no SO, intitulado: Como obter a posição do cursor no bash? . Estou postando isso aqui apenas para mostrar que eles funcionam e que o conteúdo das soluções também está na U&L.

Soluções Bash

De dentro de um script

#!/bin/bash
# based on a script from http://invisible-island.net/xterm/xterm.faq.html
exec < /dev/tty
oldstty=$(stty -g)
stty raw -echo min 0
# on my system, the following line can be replaced by the line below it
echo -en "\033[6n" > /dev/tty
# tput u7 > /dev/tty    # when TERM=xterm (and relatives)
IFS=';' read -r -d R -a pos
stty $oldstty
# change from one-based to zero based so they work with: tput cup $row $col
row=$((${pos[0]:2} - 1))    # strip off the esc-[
col=$((${pos[1]} - 1))

echo "(row,col): $row,$col"

NOTA: Alterei ligeiramente a saída!

Exemplo

$ ./rowcol.bash 
(row,col): 43,0
$ clear
$ ./rowcol.bash 
(row,col): 1,0

Shell interativo

Essa cadeia de comando trabalhou para obter as posições de linha e coluna do cursor:

$ echo -en "\E[6n";read -sdR CURPOS; CURPOS=${CURPOS#*[};echo "${CURPOS}"

Exemplo

$ echo -en "\E[6n";read -sdR CURPOS; CURPOS=${CURPOS#*[};echo "${CURPOS}"
13;1
$ clear
$ echo -en "\E[6n";read -sdR CURPOS; CURPOS=${CURPOS#*[};echo "${CURPOS}"
2;1

NOTA: Este método não parece ser utilizável em nenhum tipo de script. Mesmo comandos simples em um terminal interativo não funcionaram para mim. Por exemplo:

$ pos=$(echo -en "\E[6n";read -sdR CURPOS; CURPOS=${CURPOS#*[};echo "${CURPOS}")

apenas trava indefinidamente.

soluções dash / sh

De dentro de um script

Esta solução é para sistemas Ubuntu / Debian que são fornecidos com estoque dash, compatível com POSIX. Por esse motivo, o readcomando não suporta a -dalternância entre outras diferenças.

Para contornar isso, existe esta solução que usa um sleep 1no lugar do -dcomutador. Isso não é o ideal, mas oferece pelo menos uma solução funcional.

#!/bin/sh

exec < /dev/tty
oldstty=$(stty -g)
stty raw -echo min 0
tput u7 > /dev/tty
sleep 1
IFS=';' read -r row col
stty $oldstty

row=$(expr $(expr substr $row 3 99) - 1)        # Strip leading escape off
col=$(expr ${col%R} - 1)                        # Strip trailing 'R' off

echo "(row,col): $col,$row"

Exemplo

$ ./rowcol.sh 
(row,col): 0,24
$ clear
$ ./rowcol.sh 
(row,col): 0,1

Shell interativo

Não consegui encontrar uma solução viável que funcionasse apenas shem um shell interativo.

slm
fonte
Isso parece funcionar apenas com o bash. E não com sh. Eu pessoalmente prefiro sh. Então, como eu poderia usar isso com sh?
precisa saber é o seguinte
1
@BrainStone - deixe-me pesquisar e ver se não consigo encontrar uma maneira.
slm
sh rowcol.sh. Não importa o que você coloca na primeira linha ( #!/bin/bashou #!/bin/sh) ou o final do arquivo!
BrainStone
@BrainStone - mas acho que shé apenas um modo de compatibilidade de bash. Quando eu faço isso ( sh rowcol.bash), funciona, então não funciona para você?
slm
1
@BrainStone - você poderia criar um pseudônimo alias sh=bash?
slm
17

Usando a -popção em vez de echoeu encontrei resolveu o problema de suspensão em um script. Testado com GNU bash, version 3.00.16(1)-release (x86_64-redhat-linux-gnu).

IFS=';' read -sdR -p $'\E[6n' ROW COL;echo "${ROW#*[}"

trabalha interativamente ou em um script:

#!/usr/bin/env bash
function pos
{
    local CURPOS
    read -sdR -p $'\E[6n' CURPOS
    CURPOS=${CURPOS#*[} # Strip decoration characters <ESC>[
    echo "${CURPOS}"    # Return position in "row;col" format
}
function row
{
    local COL
    local ROW
    IFS=';' read -sdR -p $'\E[6n' ROW COL
    echo "${ROW#*[}"
}
function col
{
    local COL
    local ROW
    IFS=';' read -sdR -p $'\E[6n' ROW COL
    echo "${COL}"
}
tput sc         # Save cursor position
tput cup 5 10   # Move to row 6 col 11
POS1=$(pos)     # Get the cursor position
ROW1=$(row)
COL1=$(col)
tput cup 25 15  # Move to row 25 col 15
POS2=$(pos)     # Get the cursor position
ROW2=$(row)
COL2=$(col)
tput rc # Restore cursor position
echo $BASH_VERSION
echo $POS1 $ROW1 $COL1
echo $POS2 $ROW2 $COL2

Saídas:

$. / cursor.sh
3.00.16 (1) - liberação
6; 11 6 11
26; 16 26 16
user102015
fonte
Isso funciona muito bem. Infelizmente, no entanto, ele não funciona em processos em segundo plano e, à medida que a tela rola, a linha sob a coluna salva é alterada.
precisa saber é o seguinte
4

Você pode obter a posição do cursor via ANSI CSI DSR(Dispositivo Status Report): \e[6n. Observe que ele o retorna em um formato semelhante a ANSI CSR CUP(Posição do Cursor) que você mencionou na sua pergunta, no entanto, segue o formulário \e[n;mR(onde n é a linha e a coluna).

Mais detalhes dos códigos de escape ANSI na wikipedia .

Para se apossar do valor em uma variável, isso foi respondido no StackOverflow .

Conforme mencionado em uma resposta / comentário anterior (e detalhado no artigo da wikipedia), esses códigos nem sempre são portáteis (do terminal ao terminal e do SO ao SO). Eu ainda acho que isso é melhor tratado com termcap / maldições;)

Drav Sloan
fonte
E como eu poderia armazenar isso em uma variável?
BrainStone 26/08
Não consigo fazer funcionar. Eu sempre tenho problemas com echo -e, echo -ene read .... Isso só acontece quando o código está presente no arquivo! Eu realmente não entendo isso!
BrainStone
Parece que eu quebrei alguma configuração. echo -etrabalhou antes, mas agora não! O que pode ter causado isso e como faço para restaurá-lo?
BrainStone 27/08/13
2

Com shsintaxe POSIX :

if [ -t 0 ] && [ -t 1 ]; then
  old_settings=$(stty -g) || exit
  stty -icanon -echo min 0 time 3 || exit
  printf '\033[6n'
  pos=$(dd count=1 2> /dev/null)
  pos=${pos%R*}
  pos=${pos##*\[}
  x=${pos##*;} y=${pos%%;*}
  stty "$old_settings"
fi
Stéphane Chazelas
fonte