Obtener posición vertical del cursor

10

Esto puede sonar bastante extraño, pero sé cómo configurar la posición del cursor vertical en Bash de esta manera:

echo -e "\e[12H"

Esto mueve el cursor a la línea 12 (comenzando con 1).

Entonces, ¿cómo obtengo la posición del cursor (número de línea) usando linux bash? Sería útil si pudiera simplemente almacenar este valor en una variable para poder calcular con él.

EDITAR:

Este es el error que obtengo:

$ sh rowcol.sh
-en
    read: 9: Illegal option -d
                              test.sh: 12: Bad substitution
BrainStone
fuente
Vea también un script de ejemplo (no el más simple porque tenía restricciones adicionales).
Gilles 'SO- deja de ser malvado'

Respuestas:

5

Pude usar algunos de los ejemplos del mismo artículo sobre SO, titulado: ¿Cómo obtener la posición del cursor en bash? . Estoy publicando esto aquí solo para mostrar que funcionan y que el contenido de las soluciones también está en U&L.

Soluciones bash

Desde dentro de un guión

#!/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: ¡Cambié un poco la salida!

Ejemplo

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

Shell interactivo

Esta cadena de comandos funcionó para obtener las posiciones de fila y columna del cursor:

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

Ejemplo

$ 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 no parece ser utilizable desde ningún tipo de secuencia de comandos. Incluso los comandos simples en un terminal interactivo no funcionaron para mí. Por ejemplo:

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

solo cuelga indefinidamente.

soluciones dash / sh

Desde dentro de un guión

Esta solución es para sistemas Ubuntu / Debian que vienen en stock dash, que es compatible con POSIX. Debido a esto, el readcomando no admite el -dcambio entre otras diferencias.

Para evitar esto, existe esta solución que utiliza un sleep 1en lugar del -dinterruptor. Esto no es ideal, pero ofrece al menos una solución de trabajo.

#!/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"

Ejemplo

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

Shell interactivo

No pude encontrar una solución viable que funcionara solo shen un shell interactivo.

slm
fuente
Esto parece funcionar solo con bash. Y no con sh. Yo personalmente prefiero sh. Entonces, ¿cómo podría usar esto con sh?
BrainStone
1
@BrainStone - déjame investigar y ver si no puedo encontrar una manera.
slm
sh rowcol.sh. ¡No importa lo que pones en la primera línea ( #!/bin/basho #!/bin/sh) o qué final tiene el archivo!
BrainStone
@BrainStone, pero creo que shes solo un modo de compatibilidad bash. Cuando hago eso ( sh rowcol.bash) funciona, ¿no te funciona entonces?
slm
1
@BrainStone: ¿podrías hacer un alias alias sh=bash?
slm
17

Usando la -popción en lugar de echoencontré resuelto el problema de colgar en un script. Probado con GNU bash, version 3.00.16(1)-release (x86_64-redhat-linux-gnu).

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

funciona de forma interactiva o en un 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

Salidas:

$. / cursor.sh
3.00.16 (1) -lanzamiento
6; 11 6 11
26; 16 26 16
usuario102015
fuente
Esto funciona muy bien Sin embargo, lamentablemente no funciona en procesos en segundo plano y, a medida que se desplaza la pantalla, cambia la línea debajo de la columna guardada.
leondepeon
4

Se puede llegar a través de la posición del cursor ANSI CSI DSR(Device Status Report): \e[6n. Tenga en cuenta que lo devuelve en un formato similar a ANSI CSR CUP(Posición del cursor) que menciona en su pregunta, sin embargo, sigue la forma \e[n;mR(donde n es la fila ym la columna).

Más detalles de los códigos de escape ANSI en wikipedia .

Para obtener el valor en una variable, esto se respondió en StackOverflow .

Como se mencionó en una respuesta / comentario anterior (y se detalla en el artículo de Wikipedia), estos códigos no siempre son portátiles (de terminal a terminal y de OS a OS). Todavía creo que esto se maneja mejor con termcap / curses;)

Drav Sloan
fuente
¿Y cómo podría almacenar esto en una variable?
BrainStone
No puedo hacer que funcione. Siempre tengo problemas con echo -e, echo -eny read .... ¡Esto solo sucede cuando el código está presente en el archivo! Realmente no entiendo esto!
BrainStone
Parece que rompí alguna configuración. echo -etrabajó antes pero ahora no! ¿Qué pudo haber causado esto y cómo lo restauro?
BrainStone
2

Con shsintaxis 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
fuente