現在行と行が機能していないものを取得する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 linesそして、tput colsウィンドウの現在のサイズを文字単位で返します。ウィンドウをドラッグしてサイズを変更すると、(通常は)新しい値がインタラクティブに変わることがわかります。これはカーソル位置とは無関係です。

また、ウィンドウの左上隅は(0,0)です。位置は0から始まるので、右下隅は現在(42,140)です。

tput cub1左に移動する必要があります。すべて、、、、cub1は空白のない単一の単語ですcuf1。cub2などはありません。また、カーソルを次に移動します。cuu1cud1tput cud1最初下の行の列: tput cuu1stay同じ上記の行の列です。 (これは私の設定のバグかもしれません。)

カーソル位置を読み取るtputオプションがないと思います。終わりがどこにあるかを追跡するか、次のテキスト出力を配置する場所にカーソルを移動することはプログラマーの責任です。を使用して場所を保存し、tput scを使用してその場所を複数回復元できますtput rc

カーソル_アドレス(cup)、列_アドレス(hpa)、および行_アドレス(vpa)は、単一文字の移動よりも便利な絶対行番号と列番号(0から始まる)を使用します。

完全な端末コマンドセットは に文書化されている必要があります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 < rowsです0 ≤ col < cols

カーソルキーを使って移動してEsc終了します。ラップを処理するために左/右操作のコードを変更することは難しくありません。ワードプロセッサのように画面に印刷された文字を書くこともそれほど難しくありません。

関連情報