Obtenir la position verticale du curseur

10

Cela peut sembler assez étrange, mais je sais comment définir la position du curseur vertical dans Bash comme ceci:

echo -e "\e[12H"

Cela déplace le curseur sur la 12e ligne (en commençant par 1).

Alors, comment puis-je obtenir la position du curseur (numéro de ligne) en utilisant Linux bash? Il serait utile que je puisse simplement stocker cette valeur dans une variable afin de pouvoir calculer avec elle.

ÉDITER:

Voici l'erreur que j'obtiens:

$ sh rowcol.sh
-en
    read: 9: Illegal option -d
                              test.sh: 12: Bad substitution
BrainStone
la source
Voir aussi un exemple de script (pas le plus simple car celui-ci avait des contraintes supplémentaires).
Gilles 'SO- arrête d'être méchant'

Réponses:

5

J'ai pu utiliser certains des exemples du même article sur SO, intitulé: Comment obtenir la position du curseur dans bash? . Je poste ceci ici juste pour montrer qu'ils fonctionnent et que le contenu des solutions est également sur U&L.

Bash solutions

De l'intérieur d'un 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"

REMARQUE: j'ai légèrement modifié la sortie!

Exemple

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

Coque interactive

Cette chaîne de commandes a fonctionné pour obtenir les positions de ligne et de colonne du curseur:

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

Exemple

$ 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

REMARQUE: cette méthode ne semble pas utilisable à partir de n'importe quel type de script. Même les commandes simples dans un terminal interactif ne fonctionnaient pas pour moi. Par exemple:

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

se bloque juste indéfiniment.

solutions dash / sh

De l'intérieur d'un script

Cette solution est destinée aux systèmes Ubuntu / Debian fournis en stock dash, qui sont conformes à POSIX. Pour cette raison, la readcommande ne prend pas en charge le -dcommutateur parmi d'autres différences.

Pour contourner cela, il existe cette solution qui utilise un sleep 1à la place du -dcommutateur. Ce n'est pas idéal mais offre au moins une solution de travail.

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

Exemple

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

Coque interactive

Je n'ai pas pu trouver de solution réalisable fonctionnant uniquement shdans un shell interactif.

slm
la source
Cela ne semble fonctionner qu'avec bash. Et pas avec sh. Personnellement, je préfère sh. Alors, comment pourrais-je l'utiliser avec sh?
BrainStone
1
@BrainStone - laissez-moi faire des recherches et voir si je ne trouve pas de moyen.
slm
sh rowcol.sh. Peu importe ce que vous mettez dans la première ligne ( #!/bin/bashou #!/bin/sh) ou la fin du fichier!
BrainStone
@BrainStone - mais je pense que shc'est juste un mode de compatibilité de bash. Quand je fais ça ( sh rowcol.bash) ça marche, ça ne marche pas pour toi alors?
slm
1
@BrainStone - vous pouvez faire un alias, alias sh=bash?
slm
17

En utilisant l' -poption au lieu de echoj'ai trouvé résolu le problème de suspension dans un script. Testé avec GNU bash, version 3.00.16(1)-release (x86_64-redhat-linux-gnu).

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

fonctionne de manière interactive ou dans 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

Les sorties:

$. / cursor.sh
3.00.16 (1) - libération
6; 11 6 11
26; 16 26 16
user102015
la source
Cela fonctionne très bien. Malheureusement, cela ne fonctionne pas dans les processus d'arrière-plan, et lorsque l'écran défile, la ligne sous la colonne enregistrée change.
leondepeon
4

Vous pouvez obtenir la position du curseur via ANSI CSI DSR(Device Status Report): \e[6n. Notez qu'il le renvoie dans un format similaire à ANSI CSR CUP(Position du curseur) que vous mentionnez dans votre question, mais il suit le formulaire \e[n;mR(où n est la ligne et m la colonne).

Plus de détails sur les codes d'échappement ANSI sur wikipedia .

Pour obtenir la valeur de la valeur dans une variable, cela a été répondu sur StackOverflow .

Comme mentionné dans une réponse / un commentaire précédent (et détaillé dans l'article de wikipedia), ces codes ne sont pas toujours portables (de terminal à terminal et d'OS à OS). Je pense toujours que c'est mieux géré avec termcap / curses;)

Drav Sloan
la source
Et comment pourrais-je stocker cela dans une variable?
BrainStone
Je ne peux pas le faire fonctionner. J'ai toujours des problèmes avec echo -e, echo -enet read .... Cela ne se produit que lorsque le code est présent dans le fichier! Je ne comprends pas vraiment ça!
BrainStone
Il semble que j'ai cassé un paramètre. echo -etravaillé avant mais maintenant il ne fonctionne pas! Qu'est-ce qui a pu causer cela et comment le restaurer?
BrainStone
2

Avec la shsyntaxe 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
la source