현재 행과 줄이 변경될 때마다, 즉 위쪽, 아래쪽, 왼쪽 또는 오른쪽을 누를 때마다 인쇄하기 위해 다음 스크립트를 작성했지만 출력은 항상 동일합니다.
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 cuu1
stay동일한위 행의 열입니다. (이것은 내 구성의 버그일 수 있습니다.)
커서 위치를 읽는 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종료합니다. 래핑을 처리하기 위해 왼쪽/오른쪽 작업에 대한 코드를 수정하는 것은 어렵지 않습니다. 워드 프로세서처럼 화면에 인쇄된 문자를 쓰는 것도 그리 어렵지 않습니다.