터미널 ANSI 이스케이프 코드 완전 정리
리눅스 커널 개발자가 알아야 할 모든 ANSI 이스케이프 시퀀스. 커서 제어, 텍스트 색상, 256/RGB 컬러, 터미널 초기화까지 실전 예제와 함께 정리했습니다.
printf·echo 사용법과 \033 같은 이스케이프 표기를 알면 시작이 빠릅니다.
형광펜에 비유하면,
\033[31m 은 "지금부터 빨강 형광펜을 들어라", \033[0m 은 "형광펜을 내려라" 라는 신호입니다. 인쇄기(터미널)는 신호와 신호 사이의 글자에만 그 펜을 적용하고, 신호 자체는 종이(화면)에 찍지 않습니다.
핵심 요약
- 이스케이프(Escape) 문자
\033(=ESC, 0x1B) — 모든 ANSI 시퀀스의 시작. "다음은 글자가 아니라 명령" 이라고 터미널에 알리는 신호. - CSI (Control Sequence Introducer)
\033[— 가장 흔한 명령 군의 시작. 뒤에 숫자 파라미터와 명령 글자가 따라옴 (예:\033[31m). - SGR (Select Graphic Rendition) — 색상·굵기·밑줄 등 글자 모양을 바꾸는 명령. 끝 글자가
m.0m=리셋,30~37=전경 8 색,40~47=배경 8 색. - 리셋
\033[0m— 가장 중요. 색을 켰으면 반드시 꺼야 셸 프롬프트까지 색이 묻어나지 않습니다. - 같은 줄 갱신
\r+\033[2K— 캐리지 리턴으로 줄 시작에 돌아간 뒤 줄을 지우고 새로 출력. 프로그레스 바·스피너의 핵심.
단계별 이해
-
터미널은 단순 인쇄기가 아니라 상태 머신
터미널은 받은 바이트를 그대로 화면에 찍는 게 아니라,\033를 만나면 "명령 해석 모드" 로 전환합니다. 명령이 끝나면 다시 일반 출력 모드로 돌아옵니다. 이 전환은drivers/tty/vt/vt.c의 상태 머신 (ESnormal↔ESescape↔ESsquare) 에서 처리됩니다. -
가장 짧은 예: 빨간 글자 한 단어 출력
printf "\033[31mHello\033[0m\n"를 실행해 보세요.\033[31m으로 빨강을 켜고,Hello를 출력하고,\033[0m으로 끕니다. 만약\033[0m을 빠뜨리면 그 다음의 셸 프롬프트까지 빨갛게 보입니다. -
속성 결합: 세미콜론으로 묶기
\033[1;33;44m처럼 여러 숫자를;로 구분하면 한꺼번에 적용됩니다. 1=굵게, 33=노랑 전경, 44=청색 배경. 자주 쓰이는 조합은1;31(굵은 빨강 = 에러),1;32(굵은 녹색 = 성공),1;33(굵은 노랑 = 경고). -
같은 자리에 다시 쓰기 (프로그레스 바·스피너)
printf "\r\033[2K처리 중 %%d%%%%" $i형태로 출력하면 새 줄로 내려가지 않고 같은 줄을 계속 갱신합니다. 핵심은\n을 쓰지 않는 것 — 작업이 끝난 마지막에만 한 번 줄바꿈합니다. -
색을 끄는 환경 존중하기
출력이 파이프나 파일로 가면 ANSI 코드는 쓰레기 글자가 됩니다.$NO_COLOR환경 변수가 설정되었거나[ ! -t 1 ]일 때는 색을 끄는 것이 표준 (no-color.org) 입니다. 셸 스크립트라면RED=$([ -z "$NO_COLOR" ] && [ -t 1 ] && printf "\033[31m")식으로 토글하세요.
개요
ANSI 이스케이프 시퀀스 (ANSI Escape Sequence)는 터미널 에뮬레이터에서 커서 이동, 텍스트 색상 변경, 화면 제어 등을 수행하기 위한 표준화된 제어 코드입니다. 1981 년 ANSI X3.64 표준으로 제정된 이후 VT100 터미널을 거쳐 현대의 xterm, Linux terminal, macOS Terminal.app 까지 모든 터미널에서 지원됩니다.
리눅스 커널 개발 과정에서 printk() 로그에 색상을 추가하거나, 커널 모듈 (Kernel Module) 에서 터미널 출력을 제어할 때, 또는 디버깅 (Debugging) 도구 개발 시 ANSI 코드를 직접 사용할 기회가 많습니다. 이 문서는 모든 ANSI 코드를 체계적으로 정리한 완전 레퍼런스입니다.
- 모든 ANSI 코드는
ESC[(0x1B 0x5B) 로 시작 - 커서 이동, 색상, 지우기, 스크롤 등 6 가지 주요 그룹으로 분류
- 8 색 → 256 색 → 24 비트 RGB(1600 만 색) 까지 진화
- 리눅스 커널은
drivers/tty/vt/vt.c에서 ANSI 시퀀스 파싱
역사 및 표준
ANSI 이스케이프 시퀀스의 역사는 1970 년대 초기 비디오 터미널로 거슬러 올라갑니다.
연혁
| 연도 | 이벤트 | 의의 |
|---|---|---|
| 1970 | DEC VT52 출시 | 초기 이스케이프 시퀀스 도입 (ESC + 1 문자) |
| 1978 | DEC VT100 출시 | CSI 시퀀스 (ESC[) 도입, 현대 ANSI 의 기반 |
| 1981 | ANSI X3.64 표준 제정 | VT100 시퀀스를 미국 국가 표준으로 채택 |
| 1984 | ECMA-48 표준 | 국제 표준화 (ISO/IEC 6429) |
| 1990s | xterm-256color 등장 | 256 색 확장 시퀀스 추가 |
| 2010s | 트루컬러 (24 비트) 지원 | 1600 만 색 표현 가능 |
주요 표준 문서
- ANSI X3.64-1979 — 최초 ANSI 표준 (현재 폐기)
- ECMA-48 — European Computer Manufacturers Association 표준 (현재 유지보수)
- ISO/IEC 6429:1992 — 국제 표준, ECMA-48 과 동일
- VT100 User Guide — DEC 공식 매뉴얼, 역사적 참고
- xterm Control Sequences — 현대 xterm 의 확장 시퀀스 문서
ANSI 시퀀스 구조
모든 ANSI 이스케이프 시퀀스는 다음 구조를 따릅니다:
ESC [ Param1 ; Param2 ; ... ; ParamN Command
- ESC (0x1B) — 이스케이프 문자. 모든 제어 시퀀스의 시작
- [ (0x5B) — CSI(Control Sequence Introducer). CSI 시퀀스 시작을 알림
- Param — 10 진수 파라미터. 여러 개는 세미콜론 (
;) 으로 구분 - Command — 최종 바이트. 실행할 명령을 지정 (대문자 알파벳)
파라미터 규칙
- 파라미터 생략 시 기본값 사용 (대부분 0 또는 1)
ESC[m은ESC[0m과 동일 (속성 리셋)ESC[H는ESC[0;0H와 동일 (커서 홈 위치)- 허용되지 않는 파라미터는 무시되거나 기본 동작 수행
ESC 문자는 C 언어에서 \e 또는 \033 (8 진수), \x1b (16 진수) 로 표현합니다. 커널 코드에서는 \033 를 주로 사용합니다.
C0 제어 문자 (0x00 ~ 0x1F)
ANSI 이스케이프보다 먼저 정의된 ASCII C0 영역의 제어 문자입니다. ECMA-48 기반의 모든 터미널이 공통으로 인식합니다. ANSI 시퀀스의 시작인 ESC (0x1B) 도 여기 속합니다.
| 16 진 | 약어 / 키 | 이름 | 표준 | 동작 |
|---|---|---|---|---|
0x00 | NUL (^@) | Null | ASCII / ECMA-48 | 채움 문자 — 무시 또는 종료자 |
0x07 | BEL (^G) | Bell | ASCII / ECMA-48 | 경고음 (비프). OSC 종결자로도 사용 |
0x08 | BS (^H) | Backspace | ASCII / ECMA-48 | 커서 1 칸 왼쪽 (지우지 않음) |
0x09 | HT (^I) | Horizontal Tab | ASCII / ECMA-48 | 다음 탭 정지 위치로 (기본 8 칸 단위) |
0x0A | LF (^J) | Line Feed | ASCII / ECMA-48 | 한 줄 아래로 (Linux: 행시작 포함, raw 모드 다름) |
0x0B | VT (^K) | Vertical Tab | ASCII / ECMA-48 | 대부분 LF 와 동일하게 처리 |
0x0C | FF (^L) | Form Feed | ASCII / ECMA-48 | 일부 터미널에서 화면 지우기 (clear 효과) |
0x0D | CR (^M) | Carriage Return | ASCII / ECMA-48 | 커서를 행 시작 (열 1) 으로 이동 — 지우지 않음 |
0x0E | SO (^N) | Shift Out | ECMA-48 | G1 문자셋으로 전환 (라인 드로잉 활성화) |
0x0F | SI (^O) | Shift In | ECMA-48 | G0 문자셋으로 복귀 (기본 ASCII) |
0x11 | DC1/XON (^Q) | Device Control 1 | ASCII (flow control) | 흐름 제어 (Flow Control) — 출력 재개 |
0x13 | DC3/XOFF (^S) | Device Control 3 | ASCII (flow control) | 흐름 제어 — 출력 일시 중지 |
0x18 | CAN (^X) | Cancel | ECMA-48 | 현재 진행 중인 ESC 시퀀스 취소 (파싱 중단) |
0x1A | SUB (^Z) | Substitute | ECMA-48 | CAN 과 유사 — 시퀀스 취소 |
0x1B | ESC (^[) | Escape | ASCII / ECMA-48 | 모든 ANSI 이스케이프 시퀀스의 시작 |
0x7F | DEL | Delete | ASCII | 대부분 무시. tty 의 erase 키 매핑 (Mapping) 에 자주 쓰임 |
C1 제어 문자 (0x80 ~ 0x9F) 와 7 비트 등가
ECMA-48 은 8 비트 환경의 C1 영역에도 제어 문자를 정의합니다. 7 비트 환경 (UTF-8 포함) 에서는 ESC + 알파벳으로 등가 표현합니다. UTF-8 환경에서는 7 비트 형식을 사용해야 안전합니다 (8 비트 형식은 다바이트 문자와 충돌).
| 8 비트 | 7 비트 (ESC + ?) | 약어 | 이름 | 표준 | 동작 |
|---|---|---|---|---|---|
0x84 | ESC D | IND | Index | ECMA-48 | 커서 한 행 아래 (스크롤 가능) |
0x85 | ESC E | NEL | Next Line | ECMA-48 | 다음 행 시작으로 — CR+LF 등가 |
0x88 | ESC H | HTS | Horizontal Tab Set | ECMA-48 | 현재 열 위치를 탭 정지점으로 설정 |
0x8D | ESC M | RI | Reverse Index | ECMA-48 | 커서 한 행 위 (스크롤 가능, IND 의 역) |
0x8E | ESC N | SS2 | Single Shift 2 | ECMA-48 | 다음 1 글자만 G2 문자셋으로 |
0x8F | ESC O | SS3 | Single Shift 3 | ECMA-48 | 다음 1 글자만 G3 문자셋으로 (기능 키 응답에도 사용) |
0x90 | ESC P | DCS | Device Control String | ECMA-48 | 장치 제어 문자열 시작 (Sixel·DECRQSS 등) |
0x9B | ESC [ | CSI | Control Sequence Introducer | ECMA-48 | 가장 흔한 시퀀스 그룹 시작 — 본문 대부분 |
0x9C | ESC \\ | ST | String Terminator | ECMA-48 | DCS·OSC·SOS·PM·APC 의 정식 종결자 |
0x9D | ESC ] | OSC | Operating System Command | ECMA-48 | 터미널 제목·하이퍼링크·클립보드 등 |
0x9E | ESC ^ | PM | Privacy Message | ECMA-48 | 실제 사용 거의 없음 — 대부분 무시 |
0x9F | ESC _ | APC | Application Program Command | ECMA-48 | kitty 의 그래픽스 프로토콜 등이 사용 |
ESC 단독 시퀀스 (CSI 가 아닌)
ESC 다음에 [ 가 아닌 다른 문자가 오는 경우입니다. VT100 시대부터 정의된 핵심 시퀀스가 많습니다.
| 시퀀스 | 약어 | 이름 | 표준 | 동작 |
|---|---|---|---|---|
ESC 7 | DECSC | Save Cursor | VT100 | 커서 위치 + SGR + 문자셋 모두 저장 |
ESC 8 | DECRC | Restore Cursor | VT100 | DECSC 로 저장한 상태 복원 |
ESC c | RIS | Reset to Initial State | VT100 / ECMA-48 | 터미널 완전 초기화 — 화면·SGR·문자셋·탭 모두 리셋 |
ESC = | DECKPAM | Keypad Application Mode | VT100 | 숫자 키패드를 응용 프로그램 모드로 (특수 시퀀스 전송) |
ESC > | DECKPNM | Keypad Numeric Mode | VT100 | 숫자 키패드를 일반 숫자 모드로 (기본값) |
ESC ( B | — | G0 = ASCII | VT100 / ECMA-48 | G0 문자셋을 ASCII 로 (기본값) |
ESC ( 0 | — | G0 = DEC Special Graphics | VT100 | 라인 드로잉 (┌ ─ ┐ 등) 활성화 — tput smacs 와 동일 |
ESC ) B / ESC ) 0 | — | G1 = ASCII / Special | VT100 | G1 슬롯 설정 — SO/SI 로 전환 |
ESC * B / ESC + B | — | G2 / G3 설정 | VT220 | G2/G3 슬롯 — SS2/SS3 로 단일 글자 전환 |
ESC #3 / ESC #4 | DECDHL | Double Height Line | VT100 | 현재 행을 더블 높이의 위/아래 절반으로 |
ESC #5 | DECSWL | Single Width Line | VT100 | 일반 폭 행 (기본) |
ESC #6 | DECDWL | Double Width Line | VT100 | 현재 행을 더블 폭으로 (글자 가로 2 배) |
ESC #8 | DECALN | Screen Alignment Pattern | VT100 | 화면을 'E' 로 채움 — 정렬 테스트용 |
ESC SP F / ESC SP G | S7C1T / S8C1T | 7/8-bit Controls | VT220 | C1 컨트롤을 7/8 비트 형식으로 보낼지 선택 |
ESC n / ESC o | LS2 / LS3 | Locking Shift | VT200+ | G2/G3 를 GL (좌측 영역) 으로 잠금 (Lock) 전환 |
표준 분류 및 출처 표기 규약
이 문서의 모든 시퀀스 표는 "표준" 컬럼으로 출처를 명시합니다. 같은 시퀀스라도 표준에 따라 동작이 다를 수 있으므로, 어느 표준을 따르는지 알아야 합니다.
표준의 계보
| 표준 식별자 | 정식 명칭 / 발행 | 지위 | 적용 범위 |
|---|---|---|---|
ECMA-48 |
ECMA-48 (5th ed., 1991) / ISO/IEC 6429:1992 | 공식 국제 표준 (현행) | CSI·SGR·DCS·OSC 의 핵심 시퀀스 정의 — 거의 모든 터미널의 공통 기반 |
ANSI X3.64 |
ANSI X3.64-1979 (폐기) | 역사적 (withdrawn) | 이름은 살아남았지만 표준 본체는 ECMA-48 로 흡수·대체 |
VT100 |
DEC VT100 User Guide (1978~) | 사실상의 산업 표준 | 커서 이동·DECSC/DECRC·문자셋 전환 등 — 현대 xterm 의 호환 기반 |
VT220 / VT420 / VT510 |
DEC 후속 모델 매뉴얼 | DEC 확장 사양 | 마우스 보고·DECSLRM·키보드 모드 등 — VT100 의 상위 호환 |
xterm |
xterm Control Sequences (Thomas E. Dickey, 유지보수 중) | de-facto 모던 레퍼런스 | 대체 화면(?1049)·256 색·RGB·OSC 8(링크)·브래킷 페이스트 — 가장 포괄적 |
Linux |
console_codes(4) man page |
리눅스 콘솔 사양 | /dev/tty1 등 커널 내장 콘솔 — 16 색만 지원 / 일부 시퀀스 다르게 해석 |
iTerm2 |
iTerm2 Documentation (Proprietary OSC) | 벤더 확장 (macOS) | OSC 1337 (이미지·attention)·shell integration 등 독자 시퀀스 |
kitty |
kitty Graphics Protocol | 벤더 확장 | 이미지 프로토콜·키보드 프로토콜 (CSI u) 등 |
private |
— | 비표준 / 표준 외 | 특정 터미널만 지원 — 호환성 보장 없음. 사용 전 tput·infocmp 로 확인 권장 |
- 단일 표준 (예:
ECMA-48) — 해당 표준이 정의한 핵심 시퀀스. 거의 모든 터미널에서 동작 - 복수 표준 (예:
VT100, xterm) — 두 표준 모두 정의. 호환성 매우 높음 private— 표준 위가 아닌 벤더·레거시 사양. 폴백 (Fallback) 코드 준비 필요?가 들어간 시퀀스 (예:ESC[?25h) — DEC 가 정의한 private mode (DECSET/DECRST). xterm 이 다수 흡수
표준 간 동작 분기 (함정)
같은 시퀀스가 표준·터미널마다 다른 동작을 하거나, 한쪽에는 정의되지 않은 경우입니다. 크로스 플랫폼 (Cross-Platform) 도구를 만들 때 가장 주의해야 할 포인트입니다.
SGR 속성 — 같은 번호 다른 의미
| 시퀀스 | ECMA-48 정설 | 실제 구현 (분기) | 안전한 대안 |
|---|---|---|---|
ESC[1m |
굵게 (Bold) | Linux console: 밝은 색 (8→16 색 효과). xterm: 폰트가 굵은꼴 미지원이면 밝은 색으로 폴백 | 굵게+밝은 색을 모두 원하면 ESC[1;91m 처럼 명시 |
ESC[2m |
얇게 (Faint / Decreased Intensity) | Linux console: 무시. xterm/GNOME: 색을 50% 어둡게. 일부 폰트는 변화 없음 | 중요하면 색상으로 직접 (예: ESC[38;5;245m) |
ESC[3m |
이탤릭 (Italic) | Linux console: 무시. 다수 터미널: 폰트가 이탤릭 미지원이면 무시 | 의미 강조엔 색상·밑줄 병행 |
ESC[5m |
느린 점멸 (Slow Blink, <150회/분) | 대부분 모던 터미널: 점멸 비활성 (사용자 옵션). Linux console: 8 색 모드에서 "밝은 배경" 효과로 처리하기도 | 주의 환기엔 색·반전(ESC[7m) 사용 |
ESC[6m |
빠른 점멸 (Rapid Blink, ≥150회/분) | 거의 모든 터미널이 무시 (접근성 문제) | 대안 권장 (위와 동일) |
ESC[8m |
은폐 (Concealed) — 글자가 배경색으로 표시 | 일부 터미널만 지원. tmux 는 정상, Linux console 무시 |
비밀번호 입력은 termios ECHO off 권장 — ANSI 의존 금지 |
ESC[10m ~ ESC[19m |
대체 폰트 0~9 (Alternative Font) | 거의 모든 터미널 무시 (Linux console 만 일부 지원) | 없음 — 사용 비권장 |
ESC[20m |
Fraktur (블랙레터) — 거의 사용 안 됨 | 모든 모던 터미널 무시 | 없음 |
ESC[21m |
이중 밑줄 (Doubly Underlined) — ECMA-48 §8.3.117 | 일부 (GNOME Terminal 구버전, mintty): "굵게 해제 (Bold Off)" 로 해석. xterm·iTerm2·Konsole·Linux: 이중 밑줄 | "굵게 해제"는 ESC[22m 사용 (표준 정의) |
ESC[5:1m / ESC[5:2m |
sub-parameter 형식의 점멸 | : sub-parameter 를 지원하지 않는 터미널은 무시 |
호환성 위해 ESC[5m 단독 사용 |
ESC[39m / ESC[49m |
기본 전경/배경 색으로 복원 | 거의 모든 터미널 일치 — 단, "기본" 의 실제 색은 사용자 테마에 종속 | 특정 색을 원하면 명시 (예: ESC[37m) |
커서 위치 저장/복원 — 두 가지 방식 공존
| 시퀀스 | 표준 | 저장 범위 | 비고 |
|---|---|---|---|
ESC 7 (DECSC) |
VT100 (DEC) | 커서 위치 + SGR 속성 + 문자셋 + 자동 줄바꿈 모드 | 전체 상태 저장 — 권장 방식 |
ESC 8 (DECRC) |
VT100 (DEC) | 위 저장 상태 복원 | DECSC 와 쌍 |
ESC[s (SCOSC) |
SCO / xterm 호환 | 커서 위치만 | 속성·문자셋 저장 안 됨 — 단순 좌표 기억용 |
ESC[u (SCORC) |
SCO / xterm 호환 | 위 위치 복원 | ESC[s 와 쌍 |
ESC 7 로 저장한 뒤 ESC[u 로 복원하면 (또는 그 반대) 일부 터미널은 두 저장소를 다르게 관리하므로 의도치 않은 결과가 나올 수 있습니다. 한 가지 방식으로 통일하세요.
화면 지우기 — 표준 외 확장
| 시퀀스 | 표준 | 동작 |
|---|---|---|
ESC[0J ~ ESC[2J |
ECMA-48 (공통) | 화면 일부/전체 지우기 — 모든 터미널에서 동작 |
ESC[3J |
xterm / Linux console (확장) | 화면 + 스크롤백 버퍼 (Buffer) 삭제 — ECMA-48 비표준 |
256 색·RGB 인자 구분자 — 세미콜론 vs 콜론
이는 ECMA-48 표준 해석의 핵심 함정 중 하나입니다.
| 구분자 형식 | ECMA-48 해석 | 실제 지원 |
|---|---|---|
ESC[38;5;196m (세미콜론) |
4 개의 독립 파라미터로 해석 — 38, 5, 196, m | de-facto 표준 — 거의 모든 터미널 지원. 권장 |
ESC[38:5:196m (콜론) |
1 개 파라미터의 sub-parameter 로 해석 — ECMA-48 §5.4.2 의 정식 형식 | 일부만 지원 (kitty, alacritty, foot). xterm·VT100 미지원 |
ESC[38;2;255;0;0m (RGB, 세미콜론) |
5 개 파라미터 | de-facto 표준 |
ESC[38:2::255:0:0m (RGB, 콜론, color-space-id 빈칸) |
ECMA-48 정식 — 빈 sub-parameter 가 color space ID 자리 | kitty 등 일부만 정확 지원. 나머지는 무시 또는 오해 |
fish 셸 의 set_color) 는 콜론 형식을 출력해 구버전 터미널에서 깨집니다.
OSC 종결자 — BEL vs ST
| 형식 | 표준 | 동작 |
|---|---|---|
ESC] ... BEL (0x07 종결) |
xterm de-facto | 가장 널리 지원. BEL 은 0x07 (^G) |
ESC] ... ESC\\ (ST 종결, 7비트) |
ECMA-48 정식 | 표준 String Terminator. xterm·VTE 등 모두 지원하지만 코드가 길어짐 |
ESC] ... 0x9C (ST 8비트) |
ECMA-48 | UTF-8 환경에서 0x9C 가 다른 의미로 해석되어 깨질 수 있음 — 사용 비권장 |
대체 화면 버퍼 — ?47 / ?1047 / ?1049 차이
| 시퀀스 | 표준 | 동작 차이 |
|---|---|---|
ESC[?47h / ESC[?47l |
xterm (legacy) | 대체 버퍼 전환만 — 화면 지우기·커서 저장 없음 |
ESC[?1047h / ESC[?1047l |
xterm | 전환 + 종료 시 화면 지움 |
ESC[?1048h / ESC[?1048l |
xterm | 커서 저장/복원만 (화면 전환 없음) |
ESC[?1049h / ESC[?1049l |
xterm (현대 권장) | 1047 + 1048 통합 — 진입 시 커서 저장+버퍼 전환+화면 지움 / 종료 시 복원. vim·less 가 사용 |
커서 이동 및 제어
커서 관련 시퀀스는 터미널에서 커서 위치를 이동하거나 숨기는 데 사용합니다.
커서 위치 이동
| 시퀀스 | 약어 | 설명 | 표준 | 예제 |
|---|---|---|---|---|
ESC[H |
CUP | 커서를 홈 (1,1) 으로 이동 | ECMA-48 | printf("\033[H"); |
ESC[r;cH |
CUP | 커서를 지정 행/열로 (1 기반) | ECMA-48 | printf("\033[10;20H"); |
ESC[r;cf |
HVP | CUP 와 동등 (별칭) | ECMA-48 | printf("\033[5;5f"); |
ESC[nA |
CUU | 커서 n 행 위로 | ECMA-48 | printf("\033[3A"); |
ESC[nB |
CUD | 커서 n 행 아래로 | ECMA-48 | printf("\033[2B"); |
ESC[nC |
CUF | 커서 n 열 오른쪽으로 (Forward) | ECMA-48 | printf("\033[5C"); |
ESC[nD |
CUB | 커서 n 열 왼쪽으로 (Backward) | ECMA-48 | printf("\033[4D"); |
ESC[nE |
CNL | n 행 아래 + 행 시작 (Cursor Next Line) | ECMA-48 | printf("\033[2E"); |
ESC[nF |
CPL | n 행 위 + 행 시작 (Cursor Previous Line) | ECMA-48 | printf("\033[3F"); |
ESC[nG |
CHA | 같은 행의 n 열로 (절대 열) | ECMA-48 | printf("\033[1G"); |
ESC[nd |
VPA | 같은 열의 n 행으로 (절대 행) | ECMA-48 | printf("\033[5d"); |
ESC[6n |
DSR (CPR) | 커서 현재 위치 요청 | VT100 | 터미널이 ESC[r;cR 응답 |
커서 이동 시각화
각 커서 이동 명령이 실제 화면에서 어떤 방향과 거리만큼 움직이는지 좌표 격자 위에 표현했습니다.
커서 모양 및 가시성
| 시퀀스 | 약어 | 설명 | 표준 |
|---|---|---|---|
ESC[?25h |
DECTCEM | 커서 표시 | VT220 |
ESC[?25l |
DECTCEM | 커서 숨김 | VT220 |
ESC[?12h |
— | 커서 깜빡임 활성화 | xterm |
ESC[?12l |
— | 커서 깜빡임 비활성화 (고정) | xterm |
ESC[0 q |
DECSCUSR | 커서 모양: 사용자 기본값 | VT520 |
ESC[1 q |
DECSCUSR | 커서 모양: 점멸하는 블록 | VT520 |
ESC[2 q |
DECSCUSR | 커서 모양: 고정 블록 | VT520 |
ESC[3 q |
DECSCUSR | 커서 모양: 점멸하는 밑줄 | VT520 |
ESC[4 q |
DECSCUSR | 커서 모양: 고정 밑줄 | VT520 |
ESC[5 q |
DECSCUSR | 커서 모양: 점멸하는 수직 바 | VT520 / xterm |
ESC[6 q |
DECSCUSR | 커서 모양: 고정 수직 바 | VT520 / xterm |
고급 커서 제어 (DEC 확장)
DEC 터미널에서 도입된 확장 시퀀스로, 현대 에뮬레이터에서도 널리 지원됩니다.
| 시퀀스 | 약어 | 설명 | 표준 |
|---|---|---|---|
ESC 7 |
DECSC | 커서 위치 + SGR + 문자셋 저장 (전체 상태) | VT100 |
ESC 8 |
DECRC | DECSC 로 저장한 상태 복원 | VT100 |
ESC[s |
SCOSC | 커서 위치만 저장 | SCO / xterm |
ESC[u |
SCORC | SCOSC 위치 복원 | SCO / xterm |
ESC[?1049h |
— | 대체 화면 버퍼 활성화 (커서 저장 + 화면 지움 포함) | xterm |
ESC[?1049l |
— | 대체 화면 버퍼 비활성화 (원본 복원) | xterm |
ESC[?1h |
DECCKM | 커서 키 애플리케이션 모드 (ESC O A) |
VT100 |
ESC[?1l |
DECCKM | 커서 키 노멀 모드 (ESC[A) |
VT100 |
ESC[?7h |
DECAWM | 자동 줄바꿈 활성화 (행 끝에서 다음 줄로) | VT100 |
ESC[?7l |
DECAWM | 자동 줄바꿈 비활성화 (마지막 글자에 덮어씀) | VT100 |
ESC[?2004h |
— | 브래킷 페이스트 활성화 | xterm |
ESC[?2004l |
— | 브래킷 페이스트 비활성화 | xterm |
ESC[?1004h |
— | 포커스 이벤트 보고 (창 활성/비활성) | xterm |
ESC[?1004l |
— | 포커스 이벤트 보고 해제 | xterm |
// 커서 위치 저장 → 이동 → 복원 예제
void save_restore_cursor_example(void)
{
// 커서 현재 위치 저장
printk("\033[s");
// 임의 위치로 이동하여 작업
printk("\033[20;10H");
printk("임시 메시지 출력");
// 저장된 위치로 복원
printk("\033[u");
printk("원래 위치로 돌아옴");
}
// 대체 화면 버퍼 사용 (풀스크린 앱용)
void use_alternate_screen(void)
{
// 대체 버퍼로 전환 + 화면 지우기
printf("\033[?1049h\033[2J\033[H");
// ... 풀스크린 작업 수행 ...
// 원본 버퍼로 복원
printf("\033[?1049l");
}
vim, less, top 등 풀스크린 터미널 앱은 대체 화면 버퍼를 사용합니다. 앱 종료 시 이전 화면이 그대로 복원되어 사용자 경험이 향상됩니다.
// 커서 숨기기 → 작업 수행 → 커서 표시 예제
void hide_cursor_do_work_show(void)
{
printk("\033[?25l"); // 커서 숨김
// ... 시간 소요 작업 ...
printk("\033[?25h"); // 커서 다시 표시
}
색상 및 텍스트 속성
ANSI 색상은 3 단계로 진화했습니다: 기본 8 색 → 256 색 → 24 비트 RGB.
기본 8 색 (Standard Colors)
표준: ECMA-48 (전경 30~37, 배경 40~47) — 모든 터미널 공통 / aixterm 기원, xterm 표준화 (밝은 전경 90~97, 밝은 배경 100~107).
| 속성 | 시퀀스 | 색상 |
|---|---|---|
| 전경색 (Foreground) | ||
ESC[30m |
검정 (Black) | ■ |
ESC[31m |
적색 (Red) | ■ |
ESC[32m |
녹색 (Green) | ■ |
ESC[33m |
황색 (Yellow) | ■ |
ESC[34m |
청색 (Blue) | ■ |
ESC[35m |
자주 (Magenta) | ■ |
ESC[36m |
청록 (Cyan) | ■ |
ESC[37m |
흰색 (White) | ■ |
| 배경색 (Background) | ||
ESC[40m ~ ESC[47m |
배경색 (위 8 색 대응) | 배경만 변경 |
| 밝은 색상 (Bright) | ||
ESC[90m ~ ESC[97m |
밝은 전경색 | 더 선명한 8 색 |
ESC[100m ~ ESC[107m |
밝은 배경색 | 배경 밝게 |
16 색 팔레트 시각화
아래 SVG 는 표준 8 색 (30~37) 과 밝은 8 색 (90~97) 이 실제로 어떻게 표시되는지 보여줍니다.
텍스트 속성 (SGR — Select Graphic Rendition)
| 시퀀스 | 설명 | 표준 | 비고 |
|---|---|---|---|
ESC[0m |
모든 속성 리셋 (기본값) | ECMA-48 | 가장 중요! |
ESC[1m |
굵게 (Bold / Increased Intensity) | ECMA-48 | Linux console: 밝은 색 — 분기 참조 |
ESC[2m |
얇게 (Faint / Decreased Intensity) | ECMA-48 | Linux console 무시 |
ESC[3m |
이탤릭 (Italic) | ECMA-48 | 폰트·터미널 미지원 시 무시 |
ESC[4m |
밑줄 (Underline) | ECMA-48 | 가장 호환성 좋음 |
ESC[5m |
느린 점멸 (Slow Blink, <150회/분) | ECMA-48 | 대부분 무시 (사용자 설정에 의해) |
ESC[6m |
빠른 점멸 (Rapid Blink, ≥150회/분) | ECMA-48 | 거의 모든 터미널 무시 |
ESC[7m |
반전 (Reverse Video) | ECMA-48 | 전경/배경 색상 교환 |
ESC[8m |
은폐 (Concealed) | ECMA-48 | 일부 터미널만 지원 |
ESC[9m |
취소선 (Crossed-out / Strikethrough) | ECMA-48 | 최신 터미널 대부분 지원 |
ESC[10m |
기본 폰트로 복원 | ECMA-48 | 대부분 의미 없음 |
ESC[11m ~ ESC[19m |
대체 폰트 1 ~ 9 | ECMA-48 | 거의 미지원 — Linux console 일부 |
ESC[20m |
Fraktur (블랙레터) | ECMA-48 | 거의 모두 무시 |
ESC[21m |
이중 밑줄 (Doubly Underlined) | ECMA-48 | 일부 구현은 "굵게 해제" — 분기 참조 |
ESC[22m |
일반 강도 — 굵게·얇게 모두 해제 | ECMA-48 | "굵게 해제"의 표준 방법 |
ESC[23m |
이탤릭·Fraktur 해제 | ECMA-48 | |
ESC[24m |
밑줄·이중 밑줄 해제 | ECMA-48 | |
ESC[25m |
점멸 해제 | ECMA-48 | |
ESC[26m |
비례 간격 (Proportional Spacing) | ECMA-48 | 거의 미지원 — mintty 일부 |
ESC[27m |
반전 해제 | ECMA-48 | |
ESC[28m |
은폐 해제 (Reveal) | ECMA-48 | |
ESC[29m |
취소선 해제 | ECMA-48 | |
ESC[50m |
비례 간격 해제 | ECMA-48 | |
ESC[51m |
외곽선 (Framed) | ECMA-48 | 거의 미지원 |
ESC[52m |
원 둘러쌈 (Encircled) | ECMA-48 | 거의 미지원 |
ESC[53m |
위 밑줄 (Overlined) | ECMA-48 | 최신 터미널 일부 지원 |
ESC[54m |
외곽선·원 해제 | ECMA-48 | |
ESC[55m |
위 밑줄 해제 | ECMA-48 |
텍스트 속성 시각화
각 SGR 속성이 실제 터미널에서 어떻게 보이는지 비교한 그림입니다.
SGR 확장 속성 (Extended SGR)
ECMA-48 표준 외에 xterm·kitty 등이 추가로 정의한 SGR 시퀀스입니다.
| 시퀀스 | 설명 | 표준 | 지원 터미널 / 비고 |
|---|---|---|---|
ESC[4:0m |
밑줄 없음 (sub-parameter 형식) | kitty, vte | ESC[24m 와 동등 |
ESC[4:1m |
일반 밑줄 (Single) | kitty, vte | ESC[4m 와 동등 |
ESC[4:2m |
이중 밑줄 (Double) | kitty, vte | ESC[21m 의 명확한 형식 |
ESC[4:3m |
물결 밑줄 (Curly / Wavy) | kitty, vte, alacritty | 맞춤법 검사 표시 흉내 |
ESC[4:4m |
점선 밑줄 (Dotted) | kitty | |
ESC[4:5m |
대시 밑줄 (Dashed) | kitty | |
ESC[58;5;Nm |
밑줄 색상 (256 색 인덱스) | xterm, kitty, vte | 본문 SGR 색상과 별도 지정 |
ESC[58;2;R;G;Bm |
밑줄 색상 (RGB) | xterm, kitty, vte | 트루컬러 터미널만 |
ESC[58:5:Nm |
밑줄 색상 (256 색, 콜론 형식) | kitty | ECMA-48 sub-parameter 정식 |
ESC[59m |
밑줄 색상 기본값 복원 | xterm, kitty | |
ESC[73m |
위첨자 (Superscript) | mintty | ECMA-48 §8.3.117 옵션 |
ESC[74m |
아래첨자 (Subscript) | mintty | |
ESC[75m |
위·아래첨자 해제 | mintty | |
ESC[90m ~ ESC[97m |
밝은 전경색 (Bright Foreground 8 ~ 15) | aixterm 기원, xterm 표준화 | 거의 모든 모던 터미널 지원 |
ESC[100m ~ ESC[107m |
밝은 배경색 (Bright Background) | aixterm / xterm | 거의 모든 모던 터미널 지원 |
// 확장 속성 활용: 밑줄 색상 변경
void print_with_underline_color(const char *text)
{
// 빨간색 밑줄 + 파란색 텍스트
printk("\033[4m"); // 밑줄 활성화
printk("\033[58;5;196m"); // 밑줄 색상: 빨강 (256 색)
printk("\033[38;5;21m"); // 텍스트 색상: 파랑
printk("%s", text);
printk("\033[0m"); // 모든 속성 리셋
}
// RGB 밑줄 색상
void print_rainbow_underline(const char *text)
{
printk("\033[4m"); // 밑줄
printk("\033[58;2;255;0;0m"); // 빨간 밑줄 (RGB)
printk("\033[38;2;0;128;255m"); // 파란 텍스트
printk("%s", text);
printk("\033[0m");
}
256 색 모드
xterm-256color 에서 지원하는 256 색은 다음 구조로 구성됩니다:
- 0-15: 기본 16 색 (8 색 + 밝은 8 색)
- 16-231: 6×6×6 RGB 컬러 큐브 (각 채널 6 단계: 0, 95, 135, 175, 215, 255)
- 232-255: 회색조 (Grayscale) 24 단계
# 256 색 전경색
echo -e "\033[38;5;196m빨간색\033[0m" # 196 번: 밝은 빨강
echo -e "\033[38;5;21m파란색\033[0m" # 21 번: 밝은 파랑
echo -e "\033[38;5;232m회색\033[0m" # 232 번: 어두운 회색
# 256 색 배경색
echo -e "\033[48;5;226m노란 배경\033[0m" # 226 번: 밝은 노랑
// 커널에서 256 색 사용 예제
void print_colored_message(const char *msg)
{
// 256 색 전경색 (빨강)
printk("\033[38;5;196m");
printk("ERROR: %s", msg);
printk("\033[0m"); // 속성 리셋
}
24 비트 RGB 트루컬러
최신 터미널은 1600 만 색 (2^24) 을 지원합니다.
# RGB 전경색: R;G;B (각 0-255)
echo -e "\033[38;2;255;128;0m주황색\033[0m"
# RGB 배경색
echo -e "\033[48;2;64;64;128m남색 배경\033[0m"
// RGB 컬러 직접 지정
#define COLOR_RGB(r,g,b) "\033[38;2;" XSTRING(r) ";" XSTRING(g) ";" XSTRING(b) "m"
// 사용 예
printk(COLOR_RGB(255, 128, 64) "커스텀 색상\033[0m");
256 색 컬러 큐브 시각화
256 색의 16-231 번은 6×6×6 RGB 큐브를 형성합니다. 각 채널은 6 단계 (0, 95, 135, 175, 215, 255) 로 양자화됩니다.
# 256 색 컬러 큐브 출력 (6x6x6)
for green in 0 1 2 3 4 5; do
for red in 0 1 2 3 4 5; do
for blue in 0 1 2 3 4 5; do
color=$((16 + red*36 + green*6 + blue))
printf "\033[48;5;%dm " $color
done
printf "\033[0m "
done
printf "\\n"
done
# 회색조 (232-255) 출력
for gray in {232..255}; do
printf "\033[48;5;%dm " $gray
done
printf "\033[0m\\n"
- 큐브 인덱스:
16 + R*36 + G*6 + B(R,G,B 는 0-5) - 회색조 인덱스:
232 + gray(gray 는 0-23) - 실제 RGB 값: 각 채널 =
n * 51(단, n=0-5, 255 는 예외)
256 색 구조 시각화
256 색의 0-15, 16-231 (6×6×6 큐브), 232-255 (회색조) 영역을 SVG 로 한눈에 보여줍니다.
OSC 시퀀스 (Operating System Command)
OSC 시퀀스는 터미널 제목·링크·클립보드 등 운영체제 연동 기능을 제공합니다. 형식: ESC] Ps ; Pt ST — 여기서 Ps 는 OSC 번호, Pt 는 데이터, ST 는 종결자 (BEL=0x07 또는 ESC \\).
| OSC 번호 | 형식 | 설명 | 표준 |
|---|---|---|---|
0 |
ESC]0;제목BEL |
윈도우 제목 + 아이콘 라벨 동시 설정 | xterm |
1 |
ESC]1;라벨BEL |
아이콘 라벨만 설정 | xterm |
2 |
ESC]2;제목BEL |
윈도우 제목만 설정 | xterm |
4 |
ESC]4;idx;specBEL |
색상 팔레트 변경 — spec 은 rgb:RR/GG/BB 또는 ?(조회) |
xterm |
7 |
ESC]7;file://host/pathBEL |
현재 작업 디렉터리 (CWD) 알림 — 새 탭이 같은 위치에 열림 | vte / iTerm2 |
8 |
ESC]8;;URLESC\\textESC]8;;ESC\\ |
하이퍼링크 (클릭 가능) | VTE / iTerm2 / WT 등 |
10 / 11 / 12 |
ESC]10;colorBEL 등 |
전경(10) / 배경(11) / 커서(12) 색 변경 또는 조회 (?) |
xterm |
52 |
ESC]52;c;base64BEL |
클립보드 복사 (c=클립보드, p=프라이머리, s=선택) | xterm / kitty / WT |
104 |
ESC]104;idxBEL |
색상 팔레트 리셋 | xterm |
110 / 111 / 112 |
ESC]110 BEL 등 |
전경(110) / 배경(111) / 커서(112) 색 기본값 복원 | xterm |
133;A |
ESC]133;A BEL |
Shell integration — 프롬프트 시작 | iTerm2 / VSCode / WezTerm |
133;B |
ESC]133;B BEL |
Shell integration — 명령 입력 시작 | iTerm2 / VSCode |
133;C |
ESC]133;C BEL |
Shell integration — 명령 실행 시작 | iTerm2 / VSCode |
133;D |
ESC]133;D;exitcodeBEL |
Shell integration — 명령 종료 + 종료 코드 | iTerm2 / VSCode |
777;notify |
ESC]777;notify;title;bodyBEL |
데스크톱 알림 트리거 | urxvt / kitty |
1337 |
ESC]1337;File=...:base64BEL |
인라인 이미지 표시 (Imgcat) | iTerm2 (proprietary) |
1337;Cursor=... |
ESC]1337;CursorShape=nBEL |
커서 모양 (iTerm2 방식) | iTerm2 (proprietary) |
# 터미널 제목 설정 (bash 프롬프트용)
set_title() {
printf "\033]0;%s@%s:%s\007" "$USER" "$HOSTNAME" "$PWD"
}
# 하이퍼링크 출력 (최신 터미널)
print_link() {
local url="$1"
local text="$2"
printf "\033]8;;%s\007%s\033]8;;\007" "$url" "$text"
}
# Shell Integration (iTerm2)
prompt_start() {
printf "\033]133;A\007" # 프롬프트 시작
}
command_start() {
printf "\033]133;B\007" # 명령 실행 시작
}
command_end() {
printf "\033]133;D;%d\007" $? # 명령 종료 + 종료코드
}
화면 지우기 및 스크롤
| 시퀀스 | 약어 | 설명 | 표준 |
|---|---|---|---|
ESC[J = ESC[0J |
ED 0 | 커서 이후 → 화면 끝까지 지움 | ECMA-48 |
ESC[1J |
ED 1 | 화면 시작 → 커서 이전까지 지움 | ECMA-48 |
ESC[2J |
ED 2 | 화면 전체 지움 (가장 흔함) | ECMA-48 |
ESC[3J |
ED 3 | 화면 + 스크롤백 버퍼까지 지움 | xterm / Linux (확장) |
ESC[K = ESC[0K |
EL 0 | 커서 → 행 끝까지 지움 | ECMA-48 |
ESC[1K |
EL 1 | 행 시작 → 커서까지 지움 | ECMA-48 |
ESC[2K |
EL 2 | 행 전체 지움 (자주 사용) | ECMA-48 |
ESC[nS |
SU | 스크롤 영역을 n 행 위로 (Scroll Up) | ECMA-48 |
ESC[nT |
SD | 스크롤 영역을 n 행 아래로 (Scroll Down) | ECMA-48 |
ESC[nX |
ECH | 커서 위치부터 n 글자를 공백으로 (위치 유지) | ECMA-48 |
ESC[nP |
DCH | n 글자 삭제 (오른쪽 글자 당겨옴) | ECMA-48 |
ESC[n@ |
ICH | 빈 글자 n 개 삽입 (오른쪽 밀려남) | ECMA-48 |
※ 더 자세한 행/문자 단위 조작은 삽입·삭제·스크롤 영역 시퀀스 절 참조.
# 화면 전체 지우기 + 커서 홈
clear_screen() {
printf "\033[2J\033[H"
}
# 현재 행 지우기 + 메시지 출력 (프롬프트용)
update_line() {
printf "\033[2K\r" // 행 전체 지우기 + 행 시작
printf "처리 중... %d%%" $1
}
지우기 영역 시각화
각 지우기 명령 (ED, EL) 이 화면에서 어떤 부분을 지우는지 표시합니다. 회색 영역이 지워지는 부분, 청색 점이 커서 위치입니다.
삽입·삭제·스크롤 영역 시퀀스
커서 위치를 기준으로 글자나 행을 삽입·삭제하거나, 스크롤 영역 (Scroll Region) 을 지정하는 시퀀스입니다. 화면 일부만 갱신하는 TUI (Text User Interface) 프로그램의 핵심입니다.
| 시퀀스 | 약어 | 이름 | 표준 | 동작 |
|---|---|---|---|---|
ESC[n@ |
ICH | Insert Character | ECMA-48 | 커서 위치에 빈 글자 n 개 삽입 — 오른쪽 글자는 밀려남 (행 끝은 잘림) |
ESC[nP |
DCH | Delete Character | ECMA-48 | 커서 위치부터 n 글자 삭제 — 오른쪽 글자가 당겨옴 |
ESC[nX |
ECH | Erase Character | ECMA-48 | 커서 위치부터 n 글자를 공백으로 지움 — 글자 위치는 유지 (당기지 않음) |
ESC[nL |
IL | Insert Line | ECMA-48 | 커서가 있는 행에 빈 행 n 개 삽입 — 아래 행들은 밀려남 |
ESC[nM |
DL | Delete Line | ECMA-48 | 커서가 있는 행부터 n 행 삭제 — 아래 행들이 올라옴 |
ESC[nS |
SU | Scroll Up | ECMA-48 | 스크롤 영역을 n 행 위로 — 위쪽 내용이 사라지고 아래에 빈 행 추가 |
ESC[nT |
SD | Scroll Down | ECMA-48 | 스크롤 영역을 n 행 아래로 — 아래 내용 사라지고 위에 빈 행 추가 |
ESC[top;botr |
DECSTBM | Set Top/Bottom Margins | VT100 | 스크롤 영역 (행 단위) 지정 — 이후 LF·SU·SD 가 이 영역 내에서만 작동. 인자 생략 시 전체 화면 |
ESC[left;rights |
DECSLRM | Set Left/Right Margins | VT420 (private) | 스크롤 영역 (열 단위) 지정 — DECLRMM 활성화 필요 (ESC[?69h). 미지원 터미널은 SCOSC 로 오해 |
ESC[nI |
CHT | Cursor Horizontal Tab | ECMA-48 | n 개 탭 정지 앞으로 이동 |
ESC[nZ |
CBT | Cursor Backward Tab | ECMA-48 | n 개 탭 정지 뒤로 이동 |
ESC[nG |
CHA | Cursor Horizontal Absolute | ECMA-48 | 커서를 같은 행의 n 열로 (1 기반) |
ESC[nd |
VPA | Vertical Position Absolute | ECMA-48 | 커서를 같은 열의 n 행으로 (1 기반) |
ESC[r;cf |
HVP | Horizontal Vertical Position | ECMA-48 | ESC[r;cH (CUP) 와 동일 — 1 기반 절대 좌표 |
ESC[g / ESC[3g |
TBC | Tab Clear | ECMA-48 | 현재 열의 탭 정지 해제 / 3g 는 모든 탭 정지 해제 |
- SU/SD — 스크롤 영역 전체가 위/아래로 이동. 커서 위치 무관
- IL/DL — 커서가 있는 행부터 위/아래 행 삽입/삭제. 영향 범위가 커서 의존
DECSET / DECRST private 모드 종합표
ESC[? 로 시작하는 private mode 시퀀스 (? = DEC private parameter). h=설정, l=해제. 본문 곳곳에 흩어진 모드를 한자리에 모았습니다.
| 번호 | 약어 | 이름 | 표준 | 동작 (h=ON / l=OFF) |
|---|---|---|---|---|
?1 | DECCKM | Cursor Keys Mode | VT100 | ON: 화살표 키가 ESC O A / OFF: ESC [ A |
?3 | DECCOLM | 132/80 Column | VT100 | ON: 132 열 모드 (화면 지움) / OFF: 80 열 |
?5 | DECSCNM | Screen Mode | VT100 | ON: 반전 화면 (배경/전경 swap) / OFF: 일반 |
?6 | DECOM | Origin Mode | VT100 | ON: 좌표가 스크롤 영역 기준 / OFF: 화면 절대 |
?7 | DECAWM | Auto Wrap | VT100 | ON: 행 끝에서 자동 줄바꿈 (기본) / OFF: 끝 글자 덮어씀 |
?8 | DECARM | Auto Repeat | VT100 | ON: 키 누른 채로 연속 입력 / OFF: 1 회만 |
?9 | — | X10 Mouse | xterm (legacy) | 마우스 클릭 좌표 보고 — X10 인코딩 |
?12 | — | Cursor Blink | xterm | ON: 커서 깜빡임 / OFF: 고정 |
?25 | DECTCEM | Show Cursor | VT220 | ON: 커서 표시 / OFF: 숨김 |
?47 | — | Alt Screen (legacy) | xterm (legacy) | 대체 화면 버퍼 전환만 — 권장 비사용 (?1049 사용) |
?66 | DECNKM | Numeric Keypad | VT220 | ESC = / ESC > 와 동등 |
?67 | DECBKM | Backarrow Key | VT220 | ON: BS / OFF: DEL 전송 |
?69 | DECLRMM | Left/Right Margin | VT420 | DECSLRM 활성화 |
?1000 | — | Mouse Click | xterm | 마우스 버튼 누름·놓음 보고 (X11 인코딩) |
?1002 | — | Mouse Drag | xterm | 버튼 누른 채 이동도 보고 |
?1003 | — | Mouse Any-Motion | xterm | 버튼 무관 모든 이동 보고 (CPU 부하 큼) |
?1004 | — | Focus Events | xterm | 포커스 진입/이탈 시 ESC[I / ESC[O 전송 |
?1005 | — | UTF-8 Mouse | xterm | 좌표를 UTF-8 인코딩 (deprecated) |
?1006 | — | SGR Mouse | xterm (권장) | 좌표를 ESC[<b;x;yM 형식 — 좌표 한계 없음 |
?1015 | — | urxvt Mouse | rxvt | 10진수 좌표 — SGR 미지원 환경 대안 |
?1016 | — | SGR Pixel Mouse | xterm | 픽셀 단위 좌표 보고 |
?1047 | — | Alt Screen + Clear | xterm | 대체 버퍼 전환 + 종료 시 화면 지움 |
?1048 | — | Save/Restore Cursor | xterm | 커서만 저장/복원 (DECSC 와 유사) |
?1049 | — | Alt Screen (modern) | xterm (현대) | 1047 + 1048 통합 — vim·less 가 사용 |
?2004 | — | Bracketed Paste | xterm | 붙여넣기를 ESC[200~ ... ESC[201~ 로 감쌈 |
?2026 | — | Synchronized Output | contour, kitty, iTerm2 | 여러 출력을 1 프레임으로 합쳐 깜박임 방지 (ON/OFF 사이를 한 프레임 단위로) |
?2027 | — | Grapheme Clusters | contour, kitty | 유니코드 grapheme cluster 단위 너비 계산 |
?2031 | — | Color Scheme Update | contour | OS 다크/라이트 모드 변경 알림 수신 |
마우스 트래킹 — 인코딩 비교
모드(?1000·?1002 등)와 인코딩(?1006 등)을 함께 켜야 합니다. 인코딩별로 좌표 표현이 크게 다릅니다.
| 모드 | 응답 형식 | 좌표 한계 | 버튼 식별 |
|---|---|---|---|
X10 (?9) |
ESC[MCb Cx Cy (3 바이트, 각 +32) |
최대 223 (255-32) | 버튼 누름만 |
X11 (?1000) |
ESC[MCb Cx Cy 동일 |
최대 223 | 누름·놓음·휠 |
UTF-8 (?1005) |
ESC[M + UTF-8 인코딩 |
최대 2015 | X11 동일 — 폐기 권장 |
SGR (?1006) |
ESC[<b;x;y(M=누름/m=놓음) |
제한 없음 (10 진수) | 누름/놓음 명시 |
urxvt (?1015) |
ESC[b;x;yM |
제한 없음 | SGR 와 유사하지만 누름/놓음 구분 없음 |
# 마우스 트래킹 활성화 (현대 권장 조합)
printf "\033[?1002h\033[?1006h" # 드래그 보고 + SGR 인코딩
# 비활성화 (반드시 종료 시 호출)
printf "\033[?1002l\033[?1006l"
# 응답 예: 좌클릭 (10,5) 누름 → ESC[<0;10;5M
# 좌클릭 (10,5) 놓음 → ESC[<0;10;5m
DCS·DA·DSR — 장치 제어 / 식별 / 상태 보고
DCS (Device Control String, ESC P ... ST)
| 시퀀스 | 약어 | 이름 | 표준 | 동작 |
|---|---|---|---|---|
ESC PPaqdataESC \\ |
— | Sixel Graphics | VT240 / xterm / mlterm | 비트맵 (Bitmap) 이미지를 텍스트로 인코딩한 그래픽 (1980 년대 표준이 부활) |
ESC P $ qseqESC \\ |
DECRQSS | Request Selection or Setting | VT420 | 현재 SGR·여백·문자셋 등 설정 조회 — 응답: ESC P $ r ... |
ESC P $ pnumESC \\ |
DECRQM | Request Mode | VT420 | 특정 mode 의 ON/OFF 상태 조회 |
ESC P + qnamesESC \\ |
— | XTGETTCAP | xterm | terminfo 항목 조회 (이름은 16 진 인코딩) — 응답에 16 진 값 |
ESC P tmux ; ... ESC \\ |
— | tmux Passthrough | tmux | tmux 안에서 외부 터미널로 시퀀스 전달 — Sixel 등을 위한 우회로 |
DA — Device Attributes (장치 식별)
| 요청 | 응답 예 | 표준 | 의미 |
|---|---|---|---|
ESC[c (DA1) |
ESC[?64;1;2;6;9;15;22c 같은 형식 |
VT100 | 1차 장치 식별 — 모델·기능 비트마스크 |
ESC[>c (DA2) |
ESC[>0;276;0c (xterm: 0;ver;0) |
VT220+ | 2차 장치 식별 — 펌웨어 (Firmware) 버전·옵션 |
ESC[=c (DA3) |
ESC P!|idESC \\ |
VT420+ | 3차 장치 식별 — 16 진 ID |
DSR — Device Status Report (상태 보고)
| 요청 | 응답 | 표준 | 의미 |
|---|---|---|---|
ESC[5n | ESC[0n (정상) / ESC[3n (오류) | VT100 | 터미널 상태 점검 |
ESC[6n (CPR) | ESC[r;cR | VT100 | 커서 현재 위치 (행;열) 보고 |
ESC[?6n (DECXCPR) | ESC[?r;c;1R | VT420 | 확장 커서 위치 (페이지 (Page) 번호 포함) |
ESC[?15n | ESC[?13n 등 | VT220 | 프린터 상태 |
ESC[?25n | — | VT220 | UDK (User Defined Keys) 잠금 상태 |
ESC[t — 윈도우 조작 (xterm 확장)
ESC[nt 또는 ESC[n;a;bt 형태. xterm 이 정의한 다양한 윈도우 조작 시퀀스로, 보안상 일부는 비활성화되어 있는 경우가 많습니다.
| 인자 | 동작 | 응답 (있는 경우) |
|---|---|---|
1 | 윈도우 최소화 해제 (de-iconify) | — |
2 | 윈도우 최소화 (iconify) | — |
3;x;y | 윈도우 (x,y) 픽셀 위치로 이동 | — |
4;h;w | 윈도우 픽셀 크기 조정 (높이;너비) | — |
5 | 윈도우 raise (앞으로) | — |
6 | 윈도우 lower (뒤로) | — |
7 | 윈도우 다시 그리기 | — |
8;r;c | 화면 행/열 크기 조정 | — |
11 | 윈도우 상태 보고 (열림=1, 최소화=2) | ESC[1t 또는 ESC[2t |
13 | 윈도우 위치 (픽셀) | ESC[3;x;yt |
14 | 윈도우 크기 (픽셀) | ESC[4;h;wt |
18 | 화면 크기 (행/열) 보고 | ESC[8;r;ct |
19 | 모니터 크기 (행/열) 보고 | ESC[9;r;ct |
20 | 아이콘 라벨 보고 | OSC L 형식 |
21 | 윈도우 제목 보고 | OSC l 형식 |
22;0 / 22;1 / 22;2 | 제목 스택에 push (양쪽/아이콘만/제목만) | — |
23;0 / 23;1 / 23;2 | 제목 스택에서 pop | — |
ESC[21t (제목 보고) 는 악성 페이지가 사용자가 모르는 사이에 셸 프롬프트의 제목을 읽어 정보를 유출시킬 수 있어 비활성화한 터미널이 많습니다. xterm.allowTitleOps: false 자원이나 다른 터미널의 보안 옵션을 확인하세요.
함수 키 / 특수 키 입력 시퀀스
지금까지의 시퀀스는 모두 프로그램 → 터미널 (출력 방향) 이었습니다. 반대로 사용자가 키를 누르면 터미널 → 프로그램 으로 시퀀스가 전송됩니다. 이 표는 cat · sed -n l 등으로 확인할 수 있습니다.
| 키 | VT100 (커서 노멀) | VT100 (커서 앱 모드) | xterm | linux console |
|---|---|---|---|---|
| ↑ | ESC[A | ESC O A | 같음 | ESC[A |
| ↓ | ESC[B | ESC O B | 같음 | ESC[B |
| → | ESC[C | ESC O C | 같음 | ESC[C |
| ← | ESC[D | ESC O D | 같음 | ESC[D |
| Home | ESC[H 또는 ESC[1~ | ESC O H | ESC[H / ESC[1~ | ESC[1~ |
| End | ESC[F 또는 ESC[4~ | ESC O F | ESC[F / ESC[4~ | ESC[4~ |
| Insert | ESC[2~ | — | ESC[2~ | ESC[2~ |
| Delete | ESC[3~ | — | ESC[3~ | ESC[3~ |
| PgUp | ESC[5~ | — | ESC[5~ | ESC[5~ |
| PgDn | ESC[6~ | — | ESC[6~ | ESC[6~ |
| F1 | ESC O P | — | ESC O P | ESC[[A |
| F2 | ESC O Q | — | ESC O Q | ESC[[B |
| F3 | ESC O R | — | ESC O R | ESC[[C |
| F4 | ESC O S | — | ESC O S | ESC[[D |
| F5 | — | — | ESC[15~ | ESC[[E |
| F6 | — | — | ESC[17~ | ESC[17~ |
| F7 | — | — | ESC[18~ | ESC[18~ |
| F8 | — | — | ESC[19~ | ESC[19~ |
| F9 | — | — | ESC[20~ | ESC[20~ |
| F10 | — | — | ESC[21~ | ESC[21~ |
| F11 | — | — | ESC[23~ | ESC[23~ |
| F12 | — | — | ESC[24~ | ESC[24~ |
Modifier (Shift/Ctrl/Alt) 조합 — xterm 형식
xterm 은 modifier 조합을 시퀀스 안에 추가 인자로 인코딩합니다. ESC[1;mA 형식 — m 은 1+modifier 비트마스크.
| m 값 | Modifier | 예: Shift+↑ | 예: Ctrl+→ |
|---|---|---|---|
| 2 | Shift | ESC[1;2A | — |
| 3 | Alt (Meta) | ESC[1;3A | — |
| 4 | Shift+Alt | ESC[1;4A | — |
| 5 | Ctrl | — | ESC[1;5C |
| 6 | Shift+Ctrl | ESC[1;6A | — |
| 7 | Ctrl+Alt | — | ESC[1;7C |
| 8 | Shift+Ctrl+Alt | ESC[1;8A | — |
- Ctrl+A ~ Ctrl+Z 는 ASCII 0x01~0x1A 단일 바이트 (이스케이프 시퀀스 아님). 예: Ctrl+H = BS = 0x08
- Ctrl+[ = ESC = 0x1B (Escape 키와 동일 효과)
- Ctrl+I = HT = 0x09 (Tab 키와 동일)
- Ctrl+M = CR = 0x0D (Enter 키와 동일)
ESC[?2017h) 같은 확장 필요.
실전 활용 예제
커널 로그 색상 출력
// 디버그 메시지 색상 구분
#define LOG_ERROR(fmt, ...) \
printk("\033[38;5;196m[ERROR]\033[0m " fmt, ##__VA_ARGS__)
#define LOG_WARN(fmt, ...) \
printk("\033[38;5;214m[WARN]\033[0m " fmt, ##__VA_ARGS__)
#define LOG_INFO(fmt, ...) \
printk("\033[38;5;45m[INFO]\033[0m " fmt, ##__VA_ARGS__)
#define LOG_DEBUG(fmt, ...) \
printk("\033[38;5;245m[DEBUG]\033[0m " fmt, ##__VA_ARGS__)
// 사용 예
LOG_ERROR("DMA 매핑 실패: %d\\n", err);
LOG_INFO("모듈 로드 완료\\n");
프로그레스 바 (진행 표시줄)
// 단일 행 업데이트 프로그레스 바
void show_progress(int percent)
{
int i;
int filled = percent / 5; // 20 단계
printk("\033[2K\r"); // 행 지우기 + 행 시작
printk("[");
for (i = 0; i < 20; i++) {
if (i < filled)
printk("\033[38;5;46m█\033[0m"); // 녹색
else
printk("░");
}
printk("] %3d%%", percent);
}
상태 테이블 출력
// 상태 표시용 컬러 테이블
const char *status_color(int status)
{
switch (status) {
case 0: return "\033[38;5;46m"; // 성공: 초록
case -1: return "\033[38;5;196m"; // 실패: 빨강
case -2: return "\033[38;5;214m"; // 경고: 주황
default: return "\033[0m"; // 기본
}
}
// 사용 예
printk("%s%-20s\033[0m %s\\n",
status_color(ret),
"device_name",
(ret == 0) ? "OK" : "FAIL");
터미널 초기화 및 복원
# 터미널 완전 초기화 (스크립트 시작 시)
init_terminal() {
# 화면 지우기
printf "\033[2J\033[3J"
# 커서 홈
printf "\033[H"
# 스크롤백 버퍼 (Buffer) 삭제
printf "\033[3J"
}
# 원본 터미널 상태 복원 (스크립트 종료 시)
restore_terminal() {
# 모든 속성 리셋
printf "\033[0m"
# 커서 표시
printf "\033[?25h"
# 기본 색상 복원
printf "\033[39;49m"
}
실전 패턴 1 — 컬러 로그 레벨 출력
가장 흔한 패턴입니다. 로그 레벨별로 색상과 라벨을 다르게 출력해 가독성을 높입니다.
# 색상 변수 선언 (NO_COLOR 환경 변수 존중)
if [ -z "$NO_COLOR" ] && [ -t 1 ]; then
RED="\033[1;31m"; YEL="\033[1;33m"; GRN="\033[1;32m"
BLU="\033[1;34m"; DIM="\033[2;37m"; RST="\033[0m"
else
RED=""; YEL=""; GRN=""; BLU=""; DIM=""; RST=""
fi
log_error() { printf "${RED}[ERROR]${RST} %s\n" "$*"; }
log_warn() { printf "${YEL}[WARN ]${RST} %s\n" "$*"; }
log_info() { printf "${GRN}[INFO ]${RST} %s\n" "$*"; }
log_debug() { printf "${DIM}[DEBUG]${RST} %s\n" "$*"; }
실전 패턴 2 — 진행 표시 스피너
오래 걸리는 작업 중 사용자에게 “살아있음” 을 알리는 스피너입니다. \r + \033[2K 조합으로 같은 행을 계속 갱신합니다.
# 백그라운드 작업과 함께 회전 스피너 출력
spinner() {
local pid=$1
local frames='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
local i=0
printf "\033[?25l" # 커서 숨김
while kill -0 $pid 2>/dev/null; do
local ch=${frames:$((i%10)):1}
printf "\r\033[36m%s\033[0m 작업 중..." "$ch"
i=$((i+1))
sleep 0.1
done
printf "\r\033[2K\033[32m✓\033[0m 완료\n"
printf "\033[?25h" # 커서 복원
}
# 사용 예: 긴 작업을 백그라운드로 실행
long_task &
spinner $!
실전 패턴 3 — 다단계 프로그레스 바
여러 작업의 진행률을 한 번에 보여주는 다단계 프로그레스 바입니다. ESC[s / ESC[u 로 위치 저장 후 ESC[n;1H 로 절대 이동합니다.
draw_bar() {
local name="$1"; local percent=$2; local width=30
local filled=$((percent * width / 100))
local empty=$((width - filled))
printf "%-12s [" "$name"
printf "\033[42m%*s\033[0m" $filled "" # 채움 (녹색)
printf "\033[100m%*s\033[0m" $empty "" # 빈 칸 (회색)
printf "] %3d%%\n" $percent
}
show_dashboard() {
printf "\033[?25l\033[2J\033[H"
while :; do
printf "\033[H\033[1;36m=== 빌드 진행 상황 ===\033[0m\n\n"
draw_bar "컴파일" $compile_pct
draw_bar "링크" $link_pct
draw_bar "테스트" $test_pct
draw_bar "패키징" $pack_pct
[ $pack_pct -ge 100 ] && break
sleep 0.5
done
printf "\033[?25h"
}
실전 패턴 4 — 색상 박스 (Banner / Alert)
경계 문자(┌─┐│└─┘)와 배경색을 조합한 시각적 알림 박스입니다. CLI 도구의 "환영 메시지"·"경고 알림"에 자주 쓰입니다.
# 박스 그리기 (제목 + 본문)
box() {
local color="$1"; local title="$2"; local body="$3"
local width=60
printf "\033[%sm┌" "$color"
printf "─%.0s" $(seq 1 $((width-2)))
printf "┐\033[0m\n"
printf "\033[%sm│\033[1m %-*s\033[0m\033[%sm│\033[0m\n" \
"$color" $((width-3)) "$title" "$color"
printf "\033[%sm├%.0s─├\033[0m\n" "$color" $(seq 1 $((width-2)))
printf "\033[%sm│\033[0m %-*s \033[%sm│\033[0m\n" \
"$color" $((width-4)) "$body" "$color"
printf "\033[%sm└%.0s─└\033[0m\n" "$color" $(seq 1 $((width-2)))
}
box "31" "⚠ 위험" "디스크 사용량 95% 초과"
box "33" "⚡ 경고" "메모리 부족 — swap 활성"
box "32" "✓ 성공" "빌드 완료 (3m 14s)"
실전 패턴 5 — Diff 스타일 출력
git diff·diff -u 출력 스타일을 직접 흉내낼 때 쓰는 패턴입니다.
color_diff() {
while IFS= read -r line; do
case "$line" in
+++*|---*) printf "\033[1;36m%s\033[0m\n" "$line" ;; # 헤더: 청록
@@*) printf "\033[1;35m%s\033[0m\n" "$line" ;; # 헝크: 자주
+*) printf "\033[32m%s\033[0m\n" "$line" ;; # 추가: 녹색
-*) printf "\033[31m%s\033[0m\n" "$line" ;; # 삭제: 빨강
*) printf "%s\n" "$line" ;; # 그대로
esac
done
}
diff -u old.c new.c | color_diff
실전 패턴 6 — 테이블 헤더 + 행 컬러링
모니터링 도구 (htop, btop) 처럼 헤더는 반전, 행은 상태별 색을 지정합니다.
# 헤더: 흰 배경 + 검정 글자 + 굵게
printf "\033[7;1m %-5s %-15s %-8s %-10s \033[0m\n" "PID" "COMMAND" "CPU%%" "STATE"
# 데이터 행: STATE 별로 색 지정
print_row() {
local pid=$1 cmd=$2 cpu=$3 state=$4
case $state in
R) color="\033[32m" ;; # Running: 녹색
S) color="\033[36m" ;; # Sleeping: 청록
D) color="\033[33m" ;; # Disk wait: 노랑
Z) color="\033[31m" ;; # Zombie: 빨강
*) color="" ;;
esac
printf "%b %-5s %-15s %-8s %-10s\033[0m\n" \
"$color" "$pid" "$cmd" "$cpu" "$state"
}
실전 패턴 7 — 그라데이션 / 무지개 텍스트
RGB 트루컬러로 글자마다 색을 바꿔 그라데이션 효과를 냅니다. 환영 배너·축하 메시지에 자주 쓰입니다.
rainbow() {
local text="$1"
local n=${#text}
local i=0
while [ $i -lt $n ]; do
# HSV → RGB 의 단순화: i/n 을 hue 로 사용
local hue=$((i * 360 / n))
local r g b
case $((hue / 60)) in
0) r=255; g=$((hue%60 * 255/60)); b=0 ;;
1) r=$((255-(hue%60)*255/60)); g=255; b=0 ;;
2) r=0; g=255; b=$((hue%60 * 255/60)) ;;
3) r=0; g=$((255-(hue%60)*255/60)); b=255 ;;
4) r=$((hue%60 * 255/60)); g=0; b=255 ;;
5) r=255; g=0; b=$((255-(hue%60)*255/60)) ;;
esac
printf "\033[38;2;%d;%d;%dm%s" $r $g $b "${text:$i:1}"
i=$((i+1))
done
printf "\033[0m\n"
}
rainbow "Hello, Linux Kernel!"
실전 패턴 8 — 라이브 카운트다운 / 재진입 갱신
카운트다운, 시계, 실시간 (Real-time) 통계 등 같은 위치를 계속 갱신하는 패턴입니다.
countdown() {
local sec=$1
printf "\033[?25l" # 커서 숨김
while [ $sec -gt 0 ]; do
local color="32" # 녹색 (안전)
[ $sec -le 10 ] && color="33" # 노랑 (주의)
[ $sec -le 3 ] && color="31" # 빨강 (긴박)
printf "\r\033[2K남은 시간: \033[1;%sm%2d초\033[0m" "$color" $sec
sleep 1
sec=$((sec-1))
done
printf "\r\033[2K\033[1;31m⏰ 시간 종료!\033[0m\n"
printf "\033[?25h"
}
countdown 10
실전 패턴 9 — 트리 / 디렉터리 출력
tree 명령처럼 박스 그리기 문자(├ └ │)와 색상으로 계층을 표현합니다.
# 디렉터리: 청색·굵게, 실행 파일: 녹색, 심볼릭 링크: 청록
colorize_name() {
local path="$1"
if [ -d "$path" ]; then
printf "\033[1;34m%s/\033[0m" "$(basename "$path")"
elif [ -L "$path" ]; then
printf "\033[1;36m%s\033[0m -> %s" "$(basename "$path")" "$(readlink "$path")"
elif [ -x "$path" ]; then
printf "\033[1;32m%s*\033[0m" "$(basename "$path")"
else
printf "%s" "$(basename "$path")"
fi
}
실전 패턴 10 — 클릭 가능한 하이퍼링크 (OSC 8)
최신 터미널 (GNOME Terminal, iTerm2, Windows Terminal) 은 OSC 8 시퀀스로 클릭 가능한 링크를 지원합니다.
# 형식: \033]8;;URL\033\\ 표시텍스트 \033]8;;\033\\
link() {
local url="$1" text="$2"
printf "\033]8;;%s\033\\\033[4;34m%s\033[0m\033]8;;\033\\" "$url" "$text"
}
# 사용 예: 에러 메시지에 문서 링크 첨부
printf "\033[31mERROR:\033[0m parse failed — see "
link "https://example.com/docs/parser" "문서"
printf "\n"
- 로그 + 박스: 중요한 ERROR 만 박스로 강조하면 눈에 띔
- 스피너 + 카운트다운: "10 초 후 자동 재시도..." 같은 UX
- 하이퍼링크 + Diff: 변경된 파일명을 클릭하면 IDE 가 열리도록
- 프로그레스 + 컬러: 90% 이상 녹색, 50-89% 노랑, 50% 미만 빨강 등 임계치 색상
리눅스 커널 구현
리눅스 커널은 drivers/tty/vt/vt.c 에서 ANSI 이스케이프 시퀀스를 파싱합니다. 주요 함수는 다음과 같습니다:
do_con_write()— 콘솔 출력 진입점 (Entry Point)parse_escape_sequence()— ESC 시퀀스 파싱set_cursor_position()— 커서 이동 처리set_color()— SGR 색상 속성 적용
// drivers/tty/vt/vt.c 간략화 예제
static void do_con_write(struct vc_data *vc, const unsigned char *buf, int count)
{
const unsigned char *p = buf;
int i;
for (i = 0; i < count; i++) {
switch (vc->vc_state) {
case ESnormal:
if (*p == '\033') {
vc->vc_state = ESescape;
} else {
// 일반 문자 출력
do_con_putc(vc, *p);
}
break;
case ESescape:
if (*p == '[') {
vc->vc_state = ESsquare;
vc->vc_par[0] = 0;
vc->vc_npar = 0;
}
break;
case ESsquare:
if (*p >= '0' && *p <= '9') {
// 파라미터 누적
vc->vc_par[vc->vc_npar] *= 10;
vc->vc_par[vc->vc_npar] += *p - '0';
} else if (*p == ';') {
vc->vc_npar++;
} else {
// 명령 실행
execute_control(vc, *p);
vc->vc_state = ESnormal;
}
break;
}
p++;
}
}
커널 파싱 상태 머신
리눅스 커널은 상태 머신을 사용하여 ANSI 시퀀스를 파싱합니다. 각 상태는 다음과 같습니다:
| 상태 | 설명 | 전환 조건 |
|---|---|---|
ESnormal |
일반 문자 출력 상태 | ESC 입력 시 ESescape 로 |
ESescape |
ESC 수신 후 대기 |
[ 입력 시 ESsquare 로 |
ESsquare |
CSI 파라미터 수집 | 숫자: 파라미터 누적, 문자: 명령 실행 |
ESgotparm |
파라미터 수집 완료 | 명령 문자 대기 |
ESgetpars |
파라미터 구분자 처리 | ; 또는 명령 문자 |
ESfunckey |
함수 키 시퀀스 | VT100 함수 키 처리 |
SGR 색상 처리 (커널 내부)
// drivers/tty/vt/vt.c — SGR 색상 처리 간략화
static void set_color(struct vc_data *vc, int attr, int *sep)
{
switch (attr) {
case 0: // 기본값으로 리셋
vc->vc_def_color = 0x07; // 흰색/검정
vc->vc_underline = 0;
vc->vc_reverse = 0;
break;
case 1: // 굵게 (Bold)
vc->vc_bold = 1;
break;
case 4: // 밑줄 (Underline)
vc->vc_underline = 1;
break;
case 7: // 반전 (Reverse)
vc->vc_reverse = 1;
break;
case 30 ... 37: // 전경색 (Foreground)
vc->vc_fgcolor = attr - 30;
break;
case 40 ... 47: // 배경색 (Background)
vc->vc_bkcolor = attr - 40;
break;
case 39: // 기본 전경색 복원
vc->vc_fgcolor = 7;
break;
case 49: // 기본 배경색 복원
vc->vc_bkcolor = 0;
break;
}
}
/dev/tty0) 은 기본 16 색만 지원합니다. 256 색과 RGB 트루컬러는 xterm, GNOME Terminal 등 터미널 에뮬레이터에서만 사용 가능합니다.
호환성 및 지원 여부
모든 터미널이 모든 ANSI 코드를 지원하는 것은 아닙니다. 다음 명령으로 지원 기능을 확인하세요:
# 터미널 종류 확인
echo $TERM
# 컬러 지원 확인 (tput 사용)
tput colors # 8, 16, 256, 또는 16777216
# 터미널 기능 테스트
infocmp # terminfo 덤프
| 터미널 | 256 색 | RGB | 커서 모양 | 비고 |
|---|---|---|---|---|
| Linux console | ✓ | ✗ | ✗ | 기본 16 색만 |
| xterm | ✓ | ✓ | ✓ | 완전 지원 |
| GNOME Terminal | ✓ | ✓ | ✓ | libvte 기반 |
| Konsole | ✓ | ✓ | ✓ | KDE 터미널 |
| iTerm2 (macOS) | ✓ | ✓ | ✓ | 고급 기능 풍부 |
| Windows Terminal | ✓ | ✓ | ✓ | VT100 완전 지원 |
| tmux | ✓ | ✓ | ✗ | outer-terminal 의존 |
tmux/screen 특수 고려사항
터미널 멀티플렉서 (tmux, screen) 는 ANSI 시퀀스를 추가로 처리하거나 제한합니다.
tmux
- 대체 화면 버퍼: tmux 는 자체 버퍼를 사용하므로
ESC[?1049h가 비활성화될 수 있음 - 색상 제한:
default-terminal설정에 따라 256 색/RGB 지원 여부 결정 - 패스스루 시퀀스:
tmux는 일부 시퀀스를 외부 터미널로 전달하지 않음
# .tmux.conf — 256 색/RGB 활성화
set -g default-terminal "screen-256color"
# RGB 트루컬러 활성화 (tmux 3.0+)
set -ga terminal-overrides ",*256col:Tc"
# 브레이스티드 페이스트 비활성화 (일부 문제 발생 시)
set -gq terminal-features ",*:clipboard:off"
GNU screen
- capa 기능: screen 은 ANSI 시퀀스를 캡처하여 자체 버퍼에 저장
- 하드웨어 제한: screen 세션 내부에서는 256 색만 지원 (RGB 불가)
- 터미널 타입:
TERM=screen-256color권장
# .screenrc — 256 색 활성화
termcapinfo xterm* is@:ks@:ke@
setenv "TERM" "screen-256color"
# screen 세션에서 외부 터미널 기능 확인
echo $TMUX # tmux 내부인지 확인
echo $STY # screen 세션 ID
tmux show-messages -JT 로 패스스루 시퀀스를 확인하세요.
보안 고려사항 (ANSI 인젝션)
ANSI 이스케이프 시퀀스는 터미널 인젝션 공격에 악용될 수 있습니다. 신뢰할 수 없는 데이터를 출력할 때는 주의가 필요합니다.
주요 공격 벡터
| 공격 유형 | 설명 | 예제 |
|---|---|---|
| 커서 이동 조작 | 이전 출력물을 덮어쓰기하여 로그 위조 | ESC[2AESC[2K — 2 행 위로 이동 후 지우기 |
| 터미널 제목 조작 | 프롬프트를 위조하여 사용자 기만 | ESC]0;root@server# BEL |
| 클립보드 탈취 | 클립보드 내용을 원격지로 전송 | ESC]52;c;base64_data BEL |
| 명령 실행 유도 | 브레이스티드 페이스트 악용 | ESC[200~malicious_command ESC[201~ |
방어 기법
// 신뢰할 수 없는 출력에서 ANSI 시퀀스 제거
static char *strip_ansi_sequences(const char *input)
{
const char *p = input;
char *out, *buf;
int in_escape = 0;
buf = kmalloc(strlen(input) + 1, GFP_KERNEL);
if (!buf)
return NULL;
out = buf;
while (*p) {
if (*p == '\033') {
in_escape = 1;
} else if (in_escape) {
if ((*p >= 'A' && *p <= 'Z') ||
(*p >= 'a' && *p <= 'z') ||
*p == '@' || *p == '\\') {
in_escape = 0; // 시퀀스 종료
}
// ESC 시퀀스는 출력에서 제외
} else {
*out++ = *p; // 일반 문자만 복사
}
p++;
}
*out = '\0';
return buf;
}
// 사용 예: 로그 출력 전 필터링
void safe_printk(const char *fmt, ...)
{
char *buf, *safe_buf;
// ... 포맷팅 ...
safe_buf = strip_ansi_sequences(buf);
if (safe_buf) {
printk("%s", safe_buf);
kfree(safe_buf);
}
kfree(buf);
}
# Bash 에서 ANSI 시퀀스 제거 (sed 사용)
strip_ansi() {
sed -r 's/\x1B\[[0-9;]*[a-zA-Z]//g'
}
# 사용 예
echo -e "\033[31m빨간색\033[0m" | strip_ansi
# 출력: "빨간색" (색상 코드 제거됨)
# 보안 설정: 클립보드 조작 방지
printf "\033[?52l" # 클립보드 조작 비활성화 (지원 터미널)
curl | bash 패턴은 특히 위험하므로 신뢰할 수 있는 소스만 사용하세요.
빠른 참조 카드
자주 사용하는 ANSI 코드만 모친 치트시트입니다.
# ===== 기본 =====
echo -e "\033[0m" # 모든 속성 리셋 (가장 중요!)
echo -e "\033[H" # 커서 홈
echo -e "\033[2J" # 화면 전체 지우기
# ===== 색상 =====
echo -e "\033[31m" # 빨간 전경색
echo -e "\033[42m" # 녹색 배경색
echo -e "\033[1m" # 굵게
echo -e "\033[4m" # 밑줄
# ===== 256 색 =====
echo -e "\033[38;5;196m" # 256 색 전경
echo -e "\033[48;5;21m" # 256 색 배경
# ===== RGB =====
echo -e "\033[38;2;R;G;Bm" # RGB 전경
echo -e "\033[48;2;R;G;Bm" # RGB 배경
# ===== 커서 =====
echo -e "\033[?25l" # 커서 숨김
echo -e "\033[?25h" # 커서 표시
echo -e "\033[10;20H" # 10 행 20 열로 이동
# ===== 행 제어 =====
echo -e "\033[2K\r" # 행 지우기 + 행 시작 (프롬프트용)
참고자료
- 표준 문서
- 터미널 에뮬레이터 문서
- VT100.net — VT510 Reference Manual — 역사적 레퍼런스
- xterm Control Sequences — 현대 표준
- iTerm2 Escape Codes — macOS 고급 기능
- Windows Console Virtual Terminal Sequences — Windows 10+ 지원
- 리눅스 커널
- Linux console_codes(4) man page — 커널 콘솔 구현
- Linux Kernel vt.c Source — 실제 파싱 코드
- 라이브러리 및 도구
- Terminal Colors Specification (GitHub) — 색상 지원 감지
- NO_COLOR.org — 색상 비활성화 표준
- CLICOLOR — 색상 지원 환경 변수
- 보안
관련 라이브러리
ANSI 코드를 직접 다루지 않고도 색상을 활용할 수 있는 라이브러리들입니다.
| 언어 | 라이브러리 | 특징 |
|---|---|---|
| C | libcurses / ncurses |
표준 터미널 제어 라이브러리 |
| Python | colorama, rich, blessed |
크로스플랫폼, 고급 기능 |
| Node.js | chalk, ansi-escapes, ink |
모던 CLI 개발 |
| Rust | termion, crossterm, tui-rs |
타입 안전, 성능 |
| Go | fatih/color, tcell, bubbletea |
간단한 API, TUI 프레임워크 |
| Bash | tput, terminfo |
표준 유틸리티 |
| Zig | std.io.tty, zig-clap |
표준 라이브러리에 컬러 감지 내장 |
| Java | jansi, jline, picocli |
JVM 환경, Windows 호환 |
| Ruby | colorize, pastel, tty-prompt |
CLI/TUI 풍부한 생태계 |
| Swift | Rainbow, swift-argument-parser |
macOS/Linux CLI 도구 |
# tput 사용 예 (추천: 크로스플랫폼 호환)
tput setaf 1 # 빨간 전경색
echo "Error message"
tput sgr0 # 속성 리셋
# terminfo 확인
infocmp # 현재 터미널 기능 덤프
tic -x # terminfo 컴파일
# Python colorama 사용 예
from colorama import Fore, Back, Style, init
init() # Windows 호환성 초기화
print(Fore.RED + "Error: " + Style.RESET_ALL + "Something failed")
print(Back.GREEN + Fore.WHITE + " Success " + Style.RESET_ALL)
# 256 색
print(f"\033[38;5;196mBright Red\033[0m")
NO_COLOR 환경 변수가 설정되면 색상을 출력하지 않는 것이 관례입니다. CLI 도구를 개발할 때는 이 표준을 따르세요.
if [ -z "$NO_COLOR" ]; then
RED="\033[31m"
NC="\033[0m"
else
RED=""
NC=""
fi
용어 정리
- ANSI (American National Standards Institute)
- 미국 국가 표준 협회. ANSI X3.64 는 터미널 제어 코드 표준.
- CSI (Control Sequence Introducer)
ESC[(0x1B 0x5B). 파라미터를 받는 제어 시퀀스의 시작.- SGR (Select Graphic Rendition)
ESC[...m시퀀스. 색상, 굵기, 밑줄 등 텍스트 속성 설정.- DEC
- Digital Equipment Corporation. VT100 터미널 제조사. 많은 확장 시퀀스 제안.
- VT100
- 1978 년 DEC 에서 출시한 비디오 터미널. 현대 ANSI 의 기반.
- xterm
- X Window System 용 터미널 에뮬레이터. 가장 널리 사용되는 레퍼런스 구현.
- 트루컬러 (Truecolor)
- 24 비트 RGB 색상 (1600 만 색).
ESC[38;2;R;G;Bm시퀀스 사용. - 브레이스티드 페이스트 (Bracketed Paste)
- 붙여넣기 시
ESC[200~/ESC[201~로 감싸서 명령 인젝션 방지. - OSC (Operating System Command)
ESC]param;data BEL. 터미널 제목, 링크 등 OS 연동 기능.- terminfo / termcap
- 터미널 기능 데이터베이스.
tput명령이 이를 사용.
- Bash 셸 스크립팅 — ANSI 를 활용한 스크립트 작성
- 디버깅 & 트러블슈팅 — 커널 로그 색상 필터링
- C 언어 & 커널 C 관용어 — 커널 printk() 사용법
- Vim 에디터 — Vim 의 터미널 제어 기능