Linux TTY · Bash · Vim

Backspace와 개행이 다르게 보일 때

터미널이 보낸 바이트, line discipline, 편집기의 키 해석을 나누어 확인합니다.

키 입력은 여러 단계를 거칩니다

키 입력이 프로그램에 도착하는 과정
  1. 터미널 에뮬레이터키를 바이트 또는 escape sequence로 보냅니다.
  2. TTY 설정canonical 모드에서는 erase 등 특수 문자를 처리할 수 있습니다.
  3. 프로그램Vim 같은 프로그램이 키 시퀀스와 매핑을 해석합니다.

번호는 입력이 전달되는 순서입니다. 프로그램의 raw 모드에서는 TTY의 줄 편집을 사용하지 않을 수 있습니다.

printf 'TERM=%s\n' "$TERM"
stty -a
test -t 0 && printf '%s\n' 'stdin is a terminal' 

표준 입력이 파일이나 파이프면 stty가 터미널 설정을 읽지 못할 수 있습니다. stdin이 tty인 것과 셸이 대화형으로 동작하는 것은 서로 다른 조건입니다.

^H와 ^?는 다른 바이트입니다

^H는 보통 0x08, ^?는 0x7f를 표시합니다. 현재 stty erase 설정과 터미널이 보내는 코드가 맞는지 확인합니다. Ctrl 또는 Shift를 함께 누르는 방법은 프로그램과 터미널에 따라 달라 보편적인 해결책이 아닙니다.

saved_tty=$(stty -g) || exit 1
stty erase '^?'
# 여기에서 줄 편집 동작을 확인합니다.
stty "$saved_tty"

현재 설정을 저장하고 일시적으로 바꾸는 예입니다. 프로그램이 중간에 끝나는 경우에도 복원하도록 실제 스크립트에서는 종료 처리를 넣습니다. Vim에서는 :set backspace?, :set compatible?와 키 매핑도 확인합니다. Vim을 설치하기만 하면 모든 터미널 문제가 해결되는 것은 아닙니다.

CR과 LF, 색상 제어도 구분합니다

printf 'A\r\nB\n' | od -An -tx1
if [ -t 1 ]; then
    printf '\033[31m%s\033[0m\n' 'message'
else
    printf '%s\n' 'message'
fi

CR은 0x0d, LF는 0x0a입니다. 화면에서 ^M이 보이면 CR이 그대로 표시된 상황인지 확인합니다. termios의 ICRNL, ONLCR 등 변환과 파일의 실제 개행 형식은 다른 층입니다. 바이너리 데이터 전체에서 CR을 지우는 방식으로 고치면 안 됩니다. stty eof는 개행 변환이 아니라 VEOF 특수 문자 설정입니다. VEOF는 canonical 입력에서 의미가 있으므로 CR 표시 문제의 일반 해결책으로 바꾸지 않습니다.

색상 예제의 31은 SGR 전경색 설정이고 0은 속성 복원입니다. ANSI 시퀀스를 해석하지 않는 출력 대상에는 제어 바이트가 남습니다. -t는 최소한의 tty 검사이며 해당 터미널의 모든 색상 기능을 보장하지 않습니다.

확인한 문서

Linux TTY · Bash · Vim