Nhận vị trí con trỏ dọc


10

Điều này nghe có vẻ khá kỳ lạ, nhưng tôi biết cách đặt vị trí con trỏ dọc trong Bash như thế này:

echo -e "\e[12H"

Thao tác này sẽ di chuyển con trỏ đến dòng thứ 12 (bắt đầu bằng 1).

Vậy làm cách nào để có được vị trí con trỏ (số dòng) bằng linux bash? Sẽ rất hữu ích nếu tôi có thể đơn giản lưu trữ giá trị này trong một biến để tôi có thể tính toán với nó.

BIÊN TẬP:

Đây là lỗi tôi nhận được:

$ sh rowcol.sh
-en
    read: 9: Illegal option -d
                              test.sh: 12: Bad substitution

Xem thêm một tập lệnh mẫu (không đơn giản nhất vì tập lệnh này có các ràng buộc bổ sung).
Gilles 'SO- ngừng trở nên xấu xa'

Câu trả lời:


5

Tôi đã có thể sử dụng một số ví dụ từ cùng một bài viết về SO, có tiêu đề: Làm thế nào để có được vị trí con trỏ trong bash? . Tôi đang đăng bài này lên đây chỉ để cho thấy rằng chúng hoạt động và nội dung của các giải pháp cũng thực sự có trên U & L.

Giải pháp Bash

Từ trong một kịch bả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"

LƯU Ý: Tôi đã thay đổi đầu ra một chút!

Thí dụ

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

Vỏ tương tác

Chuỗi lệnh này hoạt động để lấy vị trí hàng và cột của con trỏ:

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

Thí dụ

$ 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

LƯU Ý: Phương pháp này dường như không thể sử dụng được từ bất kỳ loại tập lệnh nào. Ngay cả các lệnh đơn giản trong một thiết bị đầu cuối tương tác cũng không hoạt động với tôi. Ví dụ:

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

chỉ treo vô thời hạn.

giải pháp dash / sh

Từ trong một kịch bản

Giải pháp này dành cho các hệ thống Ubuntu / Debian đi kèm dash, tương thích POSIX. Do đó, readlệnh không hỗ trợ -dchuyển đổi giữa các khác biệt khác.

Để giải quyết vấn đề này, có giải pháp này sử dụng sleep 1thay thế -dcông tắc. Điều này không lý tưởng nhưng cung cấp ít nhất một giải pháp làm việc.

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

Thí dụ

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

Vỏ tương tác

Tôi không thể tìm thấy một giải pháp khả thi chỉ hoạt động shtrong một vỏ tương tác.


Điều này dường như chỉ hoạt động với bash. Và không phải với sh. Cá nhân tôi thích sh. Vì vậy, làm thế nào tôi có thể sử dụng này với sh?
BrainStone

1
@BrainStone - hãy để tôi nghiên cứu và xem nếu tôi không thể tìm ra cách.
slm

sh rowcol.sh. Không quan trọng bạn đặt gì ở dòng đầu tiên ( #!/bin/bashhoặc #!/bin/sh) hoặc kết thúc tệp có gì!
BrainStone

@BrainStone - nhưng tôi nghĩ shchỉ là một chế độ tương thích bash. Khi tôi làm điều đó ( sh rowcol.bash) nó hoạt động, nó không làm việc cho bạn sau đó?
slm

1
@BrainStone - bạn có thể tạo bí danh alias sh=bashkhông?
slm

17

Sử dụng -ptùy chọn thay vì echotôi thấy đã giải quyết được vấn đề treo trong tập lệnh. Đã thử nghiệm với GNU bash, version 3.00.16(1)-release (x86_64-redhat-linux-gnu).

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

hoạt động tương tác hoặc trong một kịch bản:

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

Đầu ra:

$. / con trỏ.sh
3.00.16 (1) - giải phóng
6; 11 6 11
26; 16 26 16

Điều này hoạt động rất tốt. Tuy nhiên, thật đáng buồn, nó không hoạt động trong các quy trình nền và khi màn hình cuộn, dòng bên dưới cột đã lưu thay đổi.
leondepeon

4

Bạn có thể nhận vị trí con trỏ qua ANSI CSI DSR(Báo cáo trạng thái thiết bị) : \e[6n. Lưu ý rằng nó trả về nó ở định dạng tương tự ANSI CSR CUP(Vị trí con trỏ) mà bạn đề cập trong câu hỏi của mình, tuy nhiên, nó tuân theo biểu mẫu \e[n;mR(trong đó n là hàng và m cột).

Thêm chi tiết về mã thoát ANSI trên wikipedia .

Để giữ giá trị vào một biến, điều này đã được trả lời trên StackOverflow .

Như đã đề cập trong một câu trả lời / nhận xét trước đó (và chi tiết trong bài viết trên wikipedia), các mã này không phải lúc nào cũng có thể di động (từ thiết bị đầu cuối đến thiết bị đầu cuối và hệ điều hành đến hệ điều hành). Tôi vẫn nghĩ rằng điều này được xử lý tốt hơn với termcap / lời nguyền;)


Và làm thế nào tôi có thể lưu trữ này trong một biến?
BrainStone

Tôi không thể làm cho nó hoạt động. Tôi luôn luôn gặp vấn đề với echo -e, echo -enread .... Điều này chỉ xảy ra khi mã có trong tệp! Tôi không thực sự hiểu điều này!
BrainStone

Có vẻ như tôi đã phá vỡ một số thiết lập. echo -elàm việc trước đây nhưng bây giờ thì không! Điều gì có thể đã gây ra điều này và làm thế nào để tôi khôi phục nó?
BrainStone

2

Với shcú pháp 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.