bash 脚本获取当前行和行不起作用

bash 脚本获取当前行和行不起作用

我编写了以下脚本,以便在每次更改时打印出当前行和行,也就是每次我按向上、向下、向左或向右时,但输出始终相同:

row:43:col:141

我相信这意味着屏幕的左上角,其中 0 0 位于右下角,但我不太确定。

这是我的脚本:

#!/bin/bash

echo "press a key"
tput clear
row=$(tput lines)
col=$(tput cols)
while true; do
        echo "row:$row:col:$col"
        K1=,K2=,K3=
        read -s -N1
        K1="$REPLY"
        read -s -N1 -t 0.001
        K2="$REPLY"
        read -s -N1 -t 0.001
        K3="$REPLY"
        key="$K1$K2$K3"
        case "$key" in
                $'\033[A') tput cuu 1;;
                $'\033[B') tput cud 1;;
                $'\033[C') tput cuf 1;;
                $'\033[D') tput cup $row $(($col-1));;
                *) echo "pressed a key!";;
        esac
                row=$(tput lines)
                col=$(tput cols)
done

exit 0

显然没有办法使用 tput 将光标向左移动,所以我使用了:

tput cup $row $(($col-1))

但这也不起作用,有什么想法如何解决吗?

答案1

tput linestput cols以字符为单位返回窗口的当前大小。如果拖动窗口来更改其大小,您(通常)会看到新值以交互方式更改。它们与光标位置无关。

另外,窗口的左上角是(0,0)。您的右下角当前为 (42,140),因为仓位是从零开始的。

tput cub1应该向左移动。所有cub1, cuf1, cuu1,cud1都是单个单词 - 没有空格。没有 cub2 等。另外,tput cud1将光标移动到第一的下面一行的列:tput cuu1留在相同的上面一行的列。 (不过,这可能是我的配置中的一个错误。)

我认为没有 tput 选项来读取光标位置。程序员有责任跟踪它的结束位置,或者将光标移动到下一个文本输出应该放置的位置。您可以使用 保存一个位置tput sc,并使用 恢复该位置(可能多次)tput rc

cursor_address (cup)、column_address (hpa) 和 row_address (vpa) 采用绝对行号和列号(从零开始),比单字符移动更有用。

完整的终端命令集应记录在 中man -s 5 terminfo,但tput它是一个相当笨重的工具(每个命令都有一个新过程),因此ncurses(或至少C)任何严肃的工作都需要它。

这是我对这些在我的TERM=xterm-256color.

#! /bin/bash

clear
r=$( tput lines ); c=$( tput cols )
for (( k = 1; k <= $(( r * c )); ++k )); do printf '.'; done
tput cup 0 0
printf 'At 0,0'
sleep 2

for ((k = 1; k <= 20; ++k)); do tput cud1; done
for ((k = 1; k <= 20; ++k)); do tput cuf1; done
printf 'At 20,20'
sleep 2

tput cuu1; for ((k = 1; k <= 8; ++k)); do tput cub1; done
printf 'Up one: Hello, World'
sleep 2

tput cup 10 10
printf 'At 10,10'
sleep 2

tput cud1; tput cud1; printf 'Down two: Hello, World'
sleep 5

tput cup $(( r - 1 )) 0
printf '\n\n\n\n\n'

编辑:

tput您可以通过对您希望使用的每个序列询问一次并使用重复的函数来优化代码。这样就可以避免调用这么多的外部进程,并且使代码更简单、更具可读性。

下面的代码说明了这些优化。有相当多的一次性代码来获取 tput 序列并定义实用函数。之后,实际的展示工作是简短、高效且具有相当可读性的。

进程替换在这些序列上效果不佳:例如,它删除换行符。下面的方法(阅读,拒绝所有分隔符)是稳健的。

#! /bin/bash

Tget () {   #:: Args (var, seq) -- Get a tput outcome.
    declare -n var="$1"; shift
    IFS='' read -d '' -r var < <( tput "${@}" )
}

Tget lines lines; Tget cols cols;
lines=$(( lines )); cols=$(( cols ));   #.. Bug with newlines.
Tget tUp cuu1; Tget tDown cud1; Tget tBack cub1; Tget tFrwd cuf1;
Tget tGoTo cup 35 37;
tGoTo="${tGoTo//3?/%d}"     #.. Parameterise the row and col numbers.

GoTo () {   #:: Args (line, col) -- one-based.
    printf "${tGoTo}" "$1" "$2"
}
Put () {    #:: Args (n, str) -- Repeat a string n times.
    typeset k
    for (( k = 1; k <= "$1"; ++k )); do printf '%s' "${2}"; done
}
At () {     #:: Args (line, col, text)
    GoTo "$1" "$2"; printf '%s' "$3"
}

#.. Demonstrate the resources.
GoTo 1 1; Put $(( lines * cols )) "."
At 1 11 'At 1,11:       My Title Goes Here      '; sleep 2 
Put 20 "$tDown"; Put 15 "$tFrwd"; printf 'At 21,16'; sleep 2 
Put 1 "$tUp"; Put 8 "$tBack"; printf 'Up one: Hello, World'; sleep 2 
At 10 10 'At 10,10: Updated This Space'; sleep 2 
At 10 10 'At 10,10: Updated Again     '; sleep 2 
Put 2 "$tDown"; printf 'Down two: Goodbye, World'; sleep 5 
GoTo "$lines" 1; Put 5 "$tDown"; sleep 2 
printf "That's All, Folks!" 
Put $(( lines - 30 )) "$tDown"

答案2

此代码说明了如何使用光标键X在屏幕上移动。

#!/bin/bash
clear
rows=$(tput lines) cols=$(tput cols)    # Screen size
row=$((rows/2)) col=$((cols/2))         # Initial cursor position

# Cursor key codes (would have preferred to derive these from terminfo)
cku=$'\033[A' ckd=$'\033[B' ckr=$'\033[C' ckl=$'\033[D'

# Save the terminal state and then disable echo
stty=$(stty -g)
stty -echo

# Here we go
while :
do
    # Erase previous marker
    [[ -n "$orow" ]] && [[ -n "$ocol" ]] && [[ "$orow$ocol" != "$row$col" ]] &&
        { tput civis; tput cup $orow $ocol; printf ' \b'; tput cnorm; }
    
    # Status line
    # { tput sc; tput civis; tput cup 1 1; printf "Cursor (%d,%d) key was '%s'    " $col $row "$(printf '%s' "$key" | od -xc | xargs)"; tput rc; tput cnorm; }
    { tput sc; tput civis; tput cup 1 1; printf 'Cursor (%d,%d)  ' $col $row; tput rc; tput cnorm; }

    # Place the cursor at its new point
    { tput civis; tput cup $row $col; printf 'X\b'; tput cnorm; }

    # Read upto three character codes from the keyboard
    k1= k2= k3=
    read -N1 k1; [[ "$k1" = $'\033' ]] && { read -N1 -t0.1 k2; [[ "$k2" = '[' ]] && { read -N1 -t0.1 k3; }; }
    key="$k1$k2$k3"

    # Save current position and determine new position. Prevent overflow
    orow=$row ocol=$col
    case $key in
        ("$cku")        ((row--)); [[ $row -lt 0 ]] && row=$orow ;;
        ("$ckd")        ((row++)); [[ $row -ge $rows ]] && row=$orow ;;
        ("$ckl")        ((col--)); [[ $col -lt 0 ]] && col=$ocol ;;
        ("$ckr")        ((col++)); [[ $col -ge $cols ]] && col=$ocol ;;
        ($'\033')       break ;;    # ESC = quit
    esac
done

# Reset the terminal characteristics (i.e. re-enable echo)
stty "$stty"
echo
exit 0

屏幕尺寸为0 ≤ row < rows0 ≤ col < cols

使用光标键移动、Esc退出。修改左/右操作的代码来处理环绕并不困难。让打印字符以文字处理器的方式写入屏幕也不是非常困难。

相关内容