현재 줄과 줄이 작동하지 않는 것을 얻는 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, cuu1, cud1은 공백 없이 단일 단어입니다. cub2 등은 없습니다. 또한 tput 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종료합니다. 래핑을 처리하기 위해 왼쪽/오른쪽 작업에 대한 코드를 수정하는 것은 어렵지 않습니다. 워드 프로세서처럼 화면에 인쇄된 문자를 쓰는 것도 그리 어렵지 않습니다.

관련 정보