Uzyskaj pionową pozycję kursora

10

Może to zabrzmieć dość dziwnie, ale wiem, jak ustawić pionową pozycję kursora w Bash w następujący sposób:

echo -e "\e[12H"

To przesuwa kursor do 12. linii (zaczynając od 1).

Jak więc uzyskać pozycję kursora (numer linii) za pomocą linux bash? Byłoby pomocne, gdybym mógł po prostu zapisać tę wartość w zmiennej, aby móc ją obliczyć.

EDYTOWAĆ:

To jest błąd, który otrzymuję:

$ sh rowcol.sh
-en
    read: 9: Illegal option -d
                              test.sh: 12: Bad substitution
BrainStone
źródło
Zobacz także przykładowy skrypt (nie najprostszy, ponieważ ten miał dodatkowe ograniczenia).
Gilles „SO- przestań być zły”

Odpowiedzi:

5

Byłem w stanie użyć niektórych przykładów z tego samego artykułu na temat SO, zatytułowanego: Jak uzyskać pozycję kursora w bash? . Zamieszczam to tutaj tylko po to, aby pokazać, że działają i że zawartość rozwiązań jest również w U&L.

Rozwiązania Bash

Z wnętrza skryptu

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

UWAGA: Lekko zmieniłem wyjście!

Przykład

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

Interaktywna powłoka

Ten łańcuch poleceń pracował dla uzyskania pozycji wiersza i kolumny kursora:

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

Przykład

$ 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

UWAGA: Ta metoda nie wydaje się użyteczna z żadnego rodzaju skryptu. Nawet proste polecenia w interaktywnym terminalu nie działały dla mnie. Na przykład:

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

po prostu zawiesza się na czas nieokreślony.

rozwiązania dash / sh

Z wnętrza skryptu

To rozwiązanie jest przeznaczone dla systemów Ubuntu / Debian, które są dostarczane z zapasami dash, co jest zgodne z POSIX. Z tego powodu readpolecenie nie obsługuje -dprzełączania między innymi różnicami.

Aby obejść ten problem, istnieje rozwiązanie wykorzystujące sleep 1zamiast -dprzełącznika. To nie jest idealne, ale oferuje przynajmniej działające rozwiązanie.

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

Przykład

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

Interaktywna powłoka

Nie mogłem znaleźć wykonalnego rozwiązania, które działałoby tylko shw interaktywnej powłoce.

slm
źródło
Wydaje się, że działa to tylko z bash. I nie z sh. Ja osobiście wolę sh. Jak mogę tego użyć z sh?
BrainStone
1
@BrainStone - pozwól mi zbadać i sprawdzić, czy nie mogę znaleźć sposobu.
slm
sh rowcol.sh. Nie ma znaczenia, co umieścisz w pierwszym wierszu ( #!/bin/bashlub #!/bin/sh), ani jakie ma zakończenie pliku!
BrainStone
@BrainStone - ale myślę, że shto tylko tryb zgodności z bash. Kiedy to robię ( sh rowcol.bash), to działa, czy to nie działa dla ciebie?
slm
1
@BrainStone - można zrobić alias alias sh=bash?
slm
17

Za pomocą -popcji zamiast echoznalazłem rozwiązanie problemu z zawieszaniem się w skrypcie. Testowane z GNU bash, version 3.00.16(1)-release (x86_64-redhat-linux-gnu).

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

działa interaktywnie lub w skrypcie:

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

Wyjścia:

$. / kursor.sh
Wydanie 3.00.16 (1)
6; 11 6 11
26; 16 26 16
użytkownik102015
źródło
To działa bardzo dobrze. Niestety nie działa to w procesach w tle, a wraz z przewijaniem ekranu zmienia się linia pod zapisaną kolumną.
leondepeon
4

Można uzyskać poprzez położenie kursora ANSI CSI DSR(Device status raportu): \e[6n. Zwróć uwagę, że zwraca go w formacie podobnym do ANSI CSR CUP(Pozycja kursora), o którym wspomniałeś w swoim pytaniu, jednak ma on postać \e[n;mR(gdzie n jest rzędem, a m kolumną).

Więcej szczegółów kodów ucieczkowych ANSI na wikipedii .

Aby uzyskać wartość w zmiennej, odpowiedziano na StackOverflow .

Jak wspomniano w poprzedniej odpowiedzi / komentarzu (szczegółowo opisanym w artykule na Wikipedii), kody te nie zawsze są przenośne (od terminala do terminala i systemu operacyjnego do systemu operacyjnego). Nadal uważam, że lepiej sobie z tym poradzić za pomocą termcap / curses;)

Drav Sloan
źródło
Jak mogę to zapisać w zmiennej?
BrainStone
Nie mogę tego zrobić. Zawsze mam problemy z echo -e, echo -eni read .... Dzieje się tak tylko wtedy, gdy kod jest obecny w pliku! Naprawdę tego nie rozumiem!
BrainStone
Wygląda na to, że zepsułem trochę ustawień. echo -edziałało wcześniej, ale teraz nie! Co mogło to spowodować i jak je przywrócić?
BrainStone
2

Ze shskładnią 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
źródło