Come ottenere la posizione del cursore in bash?

In uno script bash, voglio ottenere la colonna del cursore in una variabile. Sembra usare il codice di escape ANSI {ESC}[6n è l’unico modo per ottenerlo, ad esempio il seguente modo:

 # Query the cursor position echo -en '\033[6n' # Read it to a variable read -d R CURCOL # Extract the column from the variable CURCOL="${CURCOL##*;}" # We have the column in the variable echo $CURCOL 

Sfortunatamente, questo stampa i caratteri sullo standard output e voglio farlo in silenzio. Inoltre, questo non è molto portatile …

C’è un modo puro per ottenere questo?

Devi ricorrere a trucchi sporchi:

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

So che potrebbe essere risolto da sapere, ma si potrebbe semplicemente dire leggere per lavorare in silenzio con “-s”:

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

E poi CURPOS è uguale a qualcosa come “21; 3”.

Nell’interesse della portabilità ho provato a realizzare una versione compatibile con POSIX vanilla che funzionerà in shell come dash:

 #!/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 $col,$row 

… ma non riesco a trovare un’alternativa valida per bash’s ‘ read -d ‘. Senza il sonno, lo script manca completamente l’output di ritorno …

La mia (due) versione dello stesso …

Come funzione, imposta una variabile specifica, usando i comandi definiti dall’utente di ncurses:

 getCPos () { local v=() t=$(stty -g) stty -echo tput u7 IFS='[;' read -rd R -av stty $t CPos=(${v[@]:1}) } 

Di adesso:

 getCPos echo $CPos 21 echo ${CPos[1]} 1 echo ${CPos[@]} 21 1 declare -p CPos declare -a CPos=([0]="48" [1]="1") 

Nota: utilizzo il comando ncurses : tput u7 alla riga #4 nella speranza che questo rimanga più portatile rispetto all’utilizzo della stringa VT220 tramite comando: printf "\033[6n" … Non sono sicuro: comunque funzionerà con nessuno di essi:

 getCPos () { local v=() t=$(stty -g) stty -echo printf "\033[6n" IFS='[;' read -ra v -d R stty $t CPos=(${v[@]:1}) } 

funzionerà esattamente allo stesso modo, mentre sotto VT220 compatibile TERM.

Ulteriori informazioni

Puoi trovare alcuni documenti lì:

Manuale di riferimento del programmatore VT220 – Capitolo 4

4.17.2 Rapporto sullo stato del dispositivo (DSR)

 Host to VT220 (Req 4 cur pos) CSI 6 n "Please report your cursor position using a CPR (not DSR) control sequence." VT220 to host (CPR response) CSI Pv; Ph R "My cursor is positioned at _____ (Pv); _____ (Ph)." Pv = vertical position (row) Ph = horizontal position (column) 

I comandi tput sono ciò che è necessario utilizzare. semplice, veloce, nessuna uscita sullo schermo.

 #!/bin/bash col=`tput col`; line=`tput line`;