터미널 ANSI 이스케이프 코드 완전 정리

리눅스 커널 개발자가 알아야 할 모든 ANSI 이스케이프 시퀀스. 커서 제어, 텍스트 색상, 256/RGB 컬러, 터미널 초기화까지 실전 예제와 함께 정리했습니다.

전제 조건: Bash 셸 스크립팅, C 언어 & 커널 C 관용어 문서를 먼저 읽으면 도움이 됩니다. 터미널은 셸이 호출하는 입출력 (I/O) 장치이고, ANSI 이스케이프는 결국 그 장치를 향해 보내는 특수 바이트열입니다. printf·echo 사용법과 \033 같은 이스케이프 표기를 알면 시작이 빠릅니다.
일상 비유 — 형광펜과 인쇄기 명령: 터미널은 본래 글자만 한 줄씩 찍어내는 오래된 인쇄기였습니다. 색을 칠하거나 줄을 지우는 기능이 따로 없었지요. 그래서 사람들은 일반 글자에 섞여 있어도 인쇄기가 명령으로 알아듣는 약속된 부호를 만들었습니다. 그것이 ANSI 이스케이프입니다.

형광펜에 비유하면, \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 — 캐리지 리턴으로 줄 시작에 돌아간 뒤 줄을 지우고 새로 출력. 프로그레스 바·스피너의 핵심.

단계별 이해

  1. 터미널은 단순 인쇄기가 아니라 상태 머신
    터미널은 받은 바이트를 그대로 화면에 찍는 게 아니라, \033 를 만나면 "명령 해석 모드" 로 전환합니다. 명령이 끝나면 다시 일반 출력 모드로 돌아옵니다. 이 전환은 drivers/tty/vt/vt.c 의 상태 머신 (ESnormalESescapeESsquare) 에서 처리됩니다.
  2. 가장 짧은 예: 빨간 글자 한 단어 출력
    printf "\033[31mHello\033[0m\n" 를 실행해 보세요. \033[31m 으로 빨강을 켜고, Hello 를 출력하고, \033[0m 으로 끕니다. 만약 \033[0m 을 빠뜨리면 그 다음의 셸 프롬프트까지 빨갛게 보입니다.
  3. 속성 결합: 세미콜론으로 묶기
    \033[1;33;44m 처럼 여러 숫자를 ; 로 구분하면 한꺼번에 적용됩니다. 1=굵게, 33=노랑 전경, 44=청색 배경. 자주 쓰이는 조합은 1;31(굵은 빨강 = 에러), 1;32(굵은 녹색 = 성공), 1;33(굵은 노랑 = 경고).
  4. 같은 자리에 다시 쓰기 (프로그레스 바·스피너)
    printf "\r\033[2K처리 중 %%d%%%%" $i 형태로 출력하면 새 줄로 내려가지 않고 같은 줄을 계속 갱신합니다. 핵심은 \n 을 쓰지 않는 것 — 작업이 끝난 마지막에만 한 번 줄바꿈합니다.
  5. 색을 끄는 환경 존중하기
    출력이 파이프나 파일로 가면 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 만 색 표현 가능

주요 표준 문서

표준 호환성 주의: 모든 터미널이 모든 시퀀스를 지원하는 것은 아닙니다. Linux console 은 기본 16 색만 지원하며, RGB 트루컬러는 xterm, GNOME Terminal, iTerm2 등 현대 에뮬레이터에서만 사용 가능합니다.

ANSI 시퀀스 구조

모든 ANSI 이스케이프 시퀀스는 다음 구조를 따릅니다:

ESC [ Param1 ; Param2 ; ... ; ParamN Command

파라미터 규칙

주의: ESC 문자는 C 언어에서 \e 또는 \033 (8 진수), \x1b (16 진수) 로 표현합니다. 커널 코드에서는 \033 를 주로 사용합니다.

C0 제어 문자 (0x00 ~ 0x1F)

ANSI 이스케이프보다 먼저 정의된 ASCII C0 영역의 제어 문자입니다. ECMA-48 기반의 모든 터미널이 공통으로 인식합니다. ANSI 시퀀스의 시작인 ESC (0x1B) 도 여기 속합니다.

16 진 약어 / 키 이름 표준 동작
0x00NUL (^@)NullASCII / ECMA-48채움 문자 — 무시 또는 종료자
0x07BEL (^G)BellASCII / ECMA-48경고음 (비프). OSC 종결자로도 사용
0x08BS (^H)BackspaceASCII / ECMA-48커서 1 칸 왼쪽 (지우지 않음)
0x09HT (^I)Horizontal TabASCII / ECMA-48다음 탭 정지 위치로 (기본 8 칸 단위)
0x0ALF (^J)Line FeedASCII / ECMA-48한 줄 아래로 (Linux: 행시작 포함, raw 모드 다름)
0x0BVT (^K)Vertical TabASCII / ECMA-48대부분 LF 와 동일하게 처리
0x0CFF (^L)Form FeedASCII / ECMA-48일부 터미널에서 화면 지우기 (clear 효과)
0x0DCR (^M)Carriage ReturnASCII / ECMA-48커서를 행 시작 (열 1) 으로 이동 — 지우지 않음
0x0ESO (^N)Shift OutECMA-48G1 문자셋으로 전환 (라인 드로잉 활성화)
0x0FSI (^O)Shift InECMA-48G0 문자셋으로 복귀 (기본 ASCII)
0x11DC1/XON (^Q)Device Control 1ASCII (flow control)흐름 제어 (Flow Control) — 출력 재개
0x13DC3/XOFF (^S)Device Control 3ASCII (flow control)흐름 제어 — 출력 일시 중지
0x18CAN (^X)CancelECMA-48현재 진행 중인 ESC 시퀀스 취소 (파싱 중단)
0x1ASUB (^Z)SubstituteECMA-48CAN 과 유사 — 시퀀스 취소
0x1BESC (^[)EscapeASCII / ECMA-48모든 ANSI 이스케이프 시퀀스의 시작
0x7FDELDeleteASCII대부분 무시. tty 의 erase 키 매핑 (Mapping) 에 자주 쓰임

C1 제어 문자 (0x80 ~ 0x9F) 와 7 비트 등가

ECMA-48 은 8 비트 환경의 C1 영역에도 제어 문자를 정의합니다. 7 비트 환경 (UTF-8 포함) 에서는 ESC + 알파벳으로 등가 표현합니다. UTF-8 환경에서는 7 비트 형식을 사용해야 안전합니다 (8 비트 형식은 다바이트 문자와 충돌).

8 비트 7 비트 (ESC + ?) 약어 이름 표준 동작
0x84ESC DINDIndexECMA-48커서 한 행 아래 (스크롤 가능)
0x85ESC ENELNext LineECMA-48다음 행 시작으로 — CR+LF 등가
0x88ESC HHTSHorizontal Tab SetECMA-48현재 열 위치를 탭 정지점으로 설정
0x8DESC MRIReverse IndexECMA-48커서 한 행 위 (스크롤 가능, IND 의 역)
0x8EESC NSS2Single Shift 2ECMA-48다음 1 글자만 G2 문자셋으로
0x8FESC OSS3Single Shift 3ECMA-48다음 1 글자만 G3 문자셋으로 (기능 키 응답에도 사용)
0x90ESC PDCSDevice Control StringECMA-48장치 제어 문자열 시작 (Sixel·DECRQSS 등)
0x9BESC [CSIControl Sequence IntroducerECMA-48가장 흔한 시퀀스 그룹 시작 — 본문 대부분
0x9CESC \\STString TerminatorECMA-48DCS·OSC·SOS·PM·APC 의 정식 종결자
0x9DESC ]OSCOperating System CommandECMA-48터미널 제목·하이퍼링크·클립보드 등
0x9EESC ^PMPrivacy MessageECMA-48실제 사용 거의 없음 — 대부분 무시
0x9FESC _APCApplication Program CommandECMA-48kitty 의 그래픽스 프로토콜 등이 사용
UTF-8 안전성: UTF-8 인코딩에서 0x80~0x9F 바이트는 다바이트 문자의 일부일 수 있습니다. 그래서 8 비트 C1 형식 (예: 0x9B = CSI) 을 직접 출력하면 깨집니다. 항상 7 비트 형식 (ESC + 알파벳) 을 사용하세요.

ESC 단독 시퀀스 (CSI 가 아닌)

ESC 다음에 [ 가 아닌 다른 문자가 오는 경우입니다. VT100 시대부터 정의된 핵심 시퀀스가 많습니다.

시퀀스 약어 이름 표준 동작
ESC 7DECSCSave CursorVT100커서 위치 + SGR + 문자셋 모두 저장
ESC 8DECRCRestore CursorVT100DECSC 로 저장한 상태 복원
ESC cRISReset to Initial StateVT100 / ECMA-48터미널 완전 초기화 — 화면·SGR·문자셋·탭 모두 리셋
ESC =DECKPAMKeypad Application ModeVT100숫자 키패드를 응용 프로그램 모드로 (특수 시퀀스 전송)
ESC >DECKPNMKeypad Numeric ModeVT100숫자 키패드를 일반 숫자 모드로 (기본값)
ESC ( BG0 = ASCIIVT100 / ECMA-48G0 문자셋을 ASCII 로 (기본값)
ESC ( 0G0 = DEC Special GraphicsVT100라인 드로잉 (┌ ─ ┐ 등) 활성화 — tput smacs 와 동일
ESC ) B / ESC ) 0G1 = ASCII / SpecialVT100G1 슬롯 설정 — SO/SI 로 전환
ESC * B / ESC + BG2 / G3 설정VT220G2/G3 슬롯 — SS2/SS3 로 단일 글자 전환
ESC #3 / ESC #4DECDHLDouble Height LineVT100현재 행을 더블 높이의 위/아래 절반으로
ESC #5DECSWLSingle Width LineVT100일반 폭 행 (기본)
ESC #6DECDWLDouble Width LineVT100현재 행을 더블 폭으로 (글자 가로 2 배)
ESC #8DECALNScreen Alignment PatternVT100화면을 'E' 로 채움 — 정렬 테스트용
ESC SP F / ESC SP GS7C1T / S8C1T7/8-bit ControlsVT220C1 컨트롤을 7/8 비트 형식으로 보낼지 선택
ESC n / ESC oLS2 / LS3Locking ShiftVT200+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 등 일부만 정확 지원. 나머지는 무시 또는 오해
실용 권장: 호환성을 위해 세미콜론 형식을 쓰세요. ECMA-48 의 콜론 형식이 더 "정식" 이지만 호환 터미널이 적습니다. 일부 라이브러리 (예: 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 응답

커서 이동 시각화

각 커서 이동 명령이 실제 화면에서 어떤 방향과 거리만큼 움직이는지 좌표 격자 위에 표현했습니다.

커서 이동 명령 (시작 위치 = 5 행 7 열, 격자: 1 칸 = 1 문자) 1234 5678 9101112 131415 1234 5678 910 ESC[3A (3↑) ESC[2B (2↓) ESC[5C (5→) ESC[4D (4←) ESC[H (홈) ESC[10;15H (절대) ★ 시작 위치 (행 5, 열 7) — 절대 이동(점선)은 위치를, 상대 이동(실선)은 변위를 의미

커서 모양 및 가시성

시퀀스 약어 설명 표준
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) 이 실제로 어떻게 표시되는지 보여줍니다.

표준 8 색 (Foreground 30-37 / Background 40-47) 30 Black #000000 31 Red #cd3131 32 Green #0dbc79 33 Yellow #e5e510 34 Blue #2472c8 35 Magenta #bc3fbc 36 Cyan #11a8cd 37 White #e5e5e5 밝은 8 색 (Bright Foreground 90-97 / Background 100-107) 90 BrBlack #666666 91 BrRed #f14c4c 92 BrGreen #23d18b 93 BrYellow #f5f543 94 BrBlue #3b8eea 95 BrMag #d670d6 96 BrCyan #29b8db 97 BrWhite #ffffff echo -e "\033[31m""적색""\033[0m" → 표시: 적색 echo -e "\033[91m""적색""\033[0m" → 표시: 적색 (밝은)

텍스트 속성 (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 속성 시각 비교 (배경: 모의 터미널 화면) ESC[0m기본 텍스트 표시 ESC[1m굵게 표시 (Bold) ESC[2m얇게 표시 (Dim) ESC[3m이탤릭 표시 (Italic) ESC[4m밑줄 표시 (Underline) ESC[7m반전 표시 (Reverse) 반전 표시 (Reverse) ESC[9m취소선 표시 (Strikethrough) ESC[1;4m굵게 + 밑줄 (조합) ESC[3;31m이탤릭 + 빨강 ESC[1;33;44m 굵은 노랑 글자 + 청색 배경 ESC[4:3m물결 밑줄 (Curly, kitty 등) ※ 배경의 #1e1e1e 는 일반적 다크 터미널 색. 실제 표시는 터미널마다 다소 차이.

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 색은 다음 구조로 구성됩니다:

# 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"
256 색 계산 공식:
  • 큐브 인덱스: 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 로 한눈에 보여줍니다.

xterm-256color 팔레트 전체 구조 [0-15] 기본 16 색 (8 표준 + 8 밝은) 0123 4567 891011 12131415 [16-231] 6×6×6 RGB 큐브 (각 채널 6 단계: 0, 95, 135, 175, 215, 255) G=0 G=1 G=2 G=3 G=4 G=5 ↑R 행 (0,95,135,175,215,255) →B 열 | 각 슬라이스: G 채널 고정 [232-255] 회색조 24 단계 (Grayscale) 232 255 예시 변환: # 196 번 = 16 + R*36 + G*6 + B 196 - 16 = 180 = 5*36 + 0*6 + 0 → R=5(255), G=0(0), B=0(0) → 순수 빨강 21 - 16 = 5 = 0*36 + 0*6 + 5 → R=0(0), G=0(0), B=5(255) → 순수 파랑 226 - 16 = 210 = 5*36 + 5*6 + 0 → R=5(255), G=5(255),B=0(0) → 순수 노랑

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 색상 팔레트 변경 — specrgb: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" $?  # 명령 종료 + 종료코드
}
OSC 보안 주의: OSC 시퀀스는 터미널 제목 변경 등을 통해 사용자를 속일 수 있습니다. 신뢰할 수 없는 출력을 표시할 때는 OSC 필터링을 고려하세요.

화면 지우기 및 스크롤

시퀀스 약어 설명 표준
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) 이 화면에서 어떤 부분을 지우는지 표시합니다. 회색 영역이 지워지는 부분, 청색 점이 커서 위치입니다.

지우기 (Erase) 명령 — 커서(◆)는 가운데 위치 가정 ESC[0J (커서→화면 끝) 커서 이후 → 화면 끝 ESC[1J (시작→커서) 시작 → 커서 이전 ESC[2J (전체 화면) 화면 전체 (스크롤백 유지) ESC[0K (커서→행 끝) 현재 행 — 커서 이후만 ESC[1K (행 시작→커서) 현재 행 — 커서 이전 ESC[2K (행 전체) 행 전체 (커서 위치 유지) ⚙ 자주 쓰는 조합: 단일 행 갱신 (프로그레스/스피너) printf "\r\033[2K" → 캐리지 리턴 + 행 전체 지우기 printf "loading 30%%" → 새 내용 출력 (개행 없음!) → 같은 행에 계속 덮어쓰기 (스피너·프로그레스 바의 핵심 기법) ⚠ \n 을 포함하면 다음 행으로 내려가 누적됨 → 마지막에만 1 회 출력

삽입·삭제·스크롤 영역 시퀀스

커서 위치를 기준으로 글자나 행을 삽입·삭제하거나, 스크롤 영역 (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 vs IL/DL:
  • SU/SD스크롤 영역 전체가 위/아래로 이동. 커서 위치 무관
  • IL/DL커서가 있는 행부터 위/아래 행 삽입/삭제. 영향 범위가 커서 의존
로그 뷰어에서 새 줄을 위에서 아래로 스크롤하려면 SU 가 적합하고, 폼 입력에서 한 줄 추가는 IL 이 적합합니다.

DECSET / DECRST private 모드 종합표

ESC[? 로 시작하는 private mode 시퀀스 (? = DEC private parameter). h=설정, l=해제. 본문 곳곳에 흩어진 모드를 한자리에 모았습니다.

번호 약어 이름 표준 동작 (h=ON / l=OFF)
?1DECCKMCursor Keys ModeVT100ON: 화살표 키가 ESC O A / OFF: ESC [ A
?3DECCOLM132/80 ColumnVT100ON: 132 열 모드 (화면 지움) / OFF: 80 열
?5DECSCNMScreen ModeVT100ON: 반전 화면 (배경/전경 swap) / OFF: 일반
?6DECOMOrigin ModeVT100ON: 좌표가 스크롤 영역 기준 / OFF: 화면 절대
?7DECAWMAuto WrapVT100ON: 행 끝에서 자동 줄바꿈 (기본) / OFF: 끝 글자 덮어씀
?8DECARMAuto RepeatVT100ON: 키 누른 채로 연속 입력 / OFF: 1 회만
?9X10 Mousexterm (legacy)마우스 클릭 좌표 보고 — X10 인코딩
?12Cursor BlinkxtermON: 커서 깜빡임 / OFF: 고정
?25DECTCEMShow CursorVT220ON: 커서 표시 / OFF: 숨김
?47Alt Screen (legacy)xterm (legacy)대체 화면 버퍼 전환만 — 권장 비사용 (?1049 사용)
?66DECNKMNumeric KeypadVT220ESC = / ESC > 와 동등
?67DECBKMBackarrow KeyVT220ON: BS / OFF: DEL 전송
?69DECLRMMLeft/Right MarginVT420DECSLRM 활성화
?1000Mouse Clickxterm마우스 버튼 누름·놓음 보고 (X11 인코딩)
?1002Mouse Dragxterm버튼 누른 채 이동도 보고
?1003Mouse Any-Motionxterm버튼 무관 모든 이동 보고 (CPU 부하 큼)
?1004Focus Eventsxterm포커스 진입/이탈 시 ESC[I / ESC[O 전송
?1005UTF-8 Mousexterm좌표를 UTF-8 인코딩 (deprecated)
?1006SGR Mousexterm (권장)좌표를 ESC[<b;x;yM 형식 — 좌표 한계 없음
?1015urxvt Mouserxvt10진수 좌표 — SGR 미지원 환경 대안
?1016SGR Pixel Mousexterm픽셀 단위 좌표 보고
?1047Alt Screen + Clearxterm대체 버퍼 전환 + 종료 시 화면 지움
?1048Save/Restore Cursorxterm커서만 저장/복원 (DECSC 와 유사)
?1049Alt Screen (modern)xterm (현대)1047 + 1048 통합 — vim·less 가 사용
?2004Bracketed Pastexterm붙여넣기를 ESC[200~ ... ESC[201~ 로 감쌈
?2026Synchronized Outputcontour, kitty, iTerm2여러 출력을 1 프레임으로 합쳐 깜박임 방지 (ON/OFF 사이를 한 프레임 단위로)
?2027Grapheme Clusterscontour, kitty유니코드 grapheme cluster 단위 너비 계산
?2031Color Scheme UpdatecontourOS 다크/라이트 모드 변경 알림 수신

마우스 트래킹 — 인코딩 비교

모드(?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[5nESC[0n (정상) / ESC[3n (오류)VT100터미널 상태 점검
ESC[6n (CPR)ESC[r;cRVT100커서 현재 위치 (행;열) 보고
ESC[?6n (DECXCPR)ESC[?r;c;1RVT420확장 커서 위치 (페이지 (Page) 번호 포함)
ESC[?15nESC[?13nVT220프린터 상태
ESC[?25nVT220UDK (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[AESC O A같음ESC[A
ESC[BESC O B같음ESC[B
ESC[CESC O C같음ESC[C
ESC[DESC O D같음ESC[D
HomeESC[H 또는 ESC[1~ESC O HESC[H / ESC[1~ESC[1~
EndESC[F 또는 ESC[4~ESC O FESC[F / ESC[4~ESC[4~
InsertESC[2~ESC[2~ESC[2~
DeleteESC[3~ESC[3~ESC[3~
PgUpESC[5~ESC[5~ESC[5~
PgDnESC[6~ESC[6~ESC[6~
F1ESC O PESC O PESC[[A
F2ESC O QESC O QESC[[B
F3ESC O RESC O RESC[[C
F4ESC O SESC O SESC[[D
F5ESC[15~ESC[[E
F6ESC[17~ESC[17~
F7ESC[18~ESC[18~
F8ESC[19~ESC[19~
F9ESC[20~ESC[20~
F10ESC[21~ESC[21~
F11ESC[23~ESC[23~
F12ESC[24~ESC[24~

Modifier (Shift/Ctrl/Alt) 조합 — xterm 형식

xterm 은 modifier 조합을 시퀀스 안에 추가 인자로 인코딩합니다. ESC[1;mA 형식 — m 은 1+modifier 비트마스크.

m Modifier 예: Shift+↑ 예: Ctrl+→
2ShiftESC[1;2A
3Alt (Meta)ESC[1;3A
4Shift+AltESC[1;4A
5CtrlESC[1;5C
6Shift+CtrlESC[1;6A
7Ctrl+AltESC[1;7C
8Shift+Ctrl+AltESC[1;8A
Ctrl+글자 의 특수성:
  • 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 키와 동일)
이 때문에 vim 등에서 Ctrl+I 를 별도 매핑할 수 없는 제약이 있습니다 — 해결하려면 kitty 의 CSI u 프로토콜 (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" "$*"; }
실제 출력 모습 (다크 터미널) [ERROR] failed to open /dev/sda1: Permission denied [WARN ] retry attempt 3/5 — sleeping 2s [INFO ] module loaded successfully (size=128KB) [DEBUG] irq=42 vector=0x21 cpu=2 → 색상으로 한눈에 심각도 구분, $NO_COLOR 시 자동 비활성화

실전 패턴 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 $!
스피너 프레임 시퀀스 (10 프레임 = 1 사이클, 100ms 간격) t=0t+100t+200t+300 t+400t+500t+600t+700 t+800t+900 실제 출력 (같은 행 갱신): 작업 중... 완료 ← 작업 진행 중 ← 작업 종료

실전 패턴 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"
}
동시 다단 진행률 (실제 표시 모습) === 빌드 진행 상황 === 컴파일 [ ] 100% 링크 [ ] 75% 테스트 [ ] 40% 패키징 [ ] 0% → ESC[H 로 매번 홈 이동, 각 행을 ESC[2K 후 재출력 = 반짝임 없는 갱신

경계 문자(┌─┐│└─┘)와 배경색을 조합한 시각적 알림 박스입니다. 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)"
알림 박스 (위험/경고/성공) ┌──────────────────────────────────────────────────────────┐ ⚠ 위험 ├──────────────────────────────────────────────────────────┤ 디스크 사용량 95% 초과 └──────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────┐ ⚡ 경고 └──────────────────────────────────────────────────────────┘ 메모리 부족 — swap 활성 ┌──────────────────────────────────────────────────────────┐ ✓ 성공 빌드 완료 (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
Diff 출력 (헤더/헝크/추가/삭제 색상 구분) --- a/drivers/net/eth.c +++ b/drivers/net/eth.c @@ -42,7 +42,9 @@ static int eth_init(void) int ret; - ret = pci_enable_device(pdev); + ret = pci_enable_device_mem(pdev); + if (ret) goto err_disable; return 0;

실전 패턴 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"
}
반전 헤더 + 상태별 컬러 행 PID COMMAND CPU%% STATE 1234 nginx 45.2 R 5678 bash 0.1 S 9012 sshd 0.0 S 3456 dd 99.9 D 7890 crashed_proc 0.0 Z 1357 xterm 1.2 R R=실행(녹) S=대기(청) D=디스크(노) Z=좀비(빨)

실전 패턴 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!"
RGB 트루컬러 그라데이션 (글자별 hue 변화) He ll o, L in ux K er ne l! → 각 글자에 \033[38;2;R;G;Bm 으로 hue=i*360/n 색 지정

실전 패턴 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
시간이 줄어들수록 색상이 변하는 패턴 남은 시간: 30 초 → 녹색 (안전, >10s) 남은 시간: 8 초 → 노랑 (주의, ≤10s) 남은 시간: 2 초 → 빨강 (긴박, ≤3s) ⏰ 시간 종료! → \r\033[2K 로 같은 행 갱신, 임계치 따라 SGR 색상 동적 변경

실전 패턴 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
}
디렉터리 / 실행 / 링크 / 일반 파일 색상 구분 project/ ├── src/ │ ├── main.c │ └── util.c ├── build/ │ ├── myapp* │ └── latest -> v1.2.3 ├── README.md └── install.sh* → 디렉터리(굵은 청), 실행 파일(녹+*), 심볼릭 링크(청록), 일반 파일(기본)

실전 패턴 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: parse failed — see 문서 ↑ "문서" 위에 마우스 hover 시 URL 표시, 클릭 (또는 Ctrl+클릭) → 브라우저 열기 ⚠ 미지원 터미널은 일반 텍스트로 표시 (시퀀스는 무시)
패턴 조합 팁:
  • 로그 + 박스: 중요한 ERROR 만 박스로 강조하면 눈에 띔
  • 스피너 + 카운트다운: "10 초 후 자동 재시도..." 같은 UX
  • 하이퍼링크 + Diff: 변경된 파일명을 클릭하면 IDE 가 열리도록
  • 프로그레스 + 컬러: 90% 이상 녹색, 50-89% 노랑, 50% 미만 빨강 등 임계치 색상

리눅스 커널 구현

리눅스 커널은 drivers/tty/vt/vt.c 에서 ANSI 이스케이프 시퀀스를 파싱합니다. 주요 함수는 다음과 같습니다:

// 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;
    }
}
Linux console 제한: 리눅스 커널 콘솔 (/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.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

# .screenrc — 256 색 활성화
termcapinfo xterm* is@:ks@:ke@
setenv "TERM" "screen-256color"

# screen 세션에서 외부 터미널 기능 확인
echo $TMUX   # tmux 내부인지 확인
echo $STY    # screen 세션 ID
tmux/screen 내부 RGB 제한: 멀티플렉서 내부에서는 RGB 트루컬러가 외부 터미널로 전달되지 않을 수 있습니다. 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"  # 클립보드 조작 비활성화 (지원 터미널)
실제 사례: 2020 년, 일부 npm 패키지가 설치 시 ANSI 인젝션으로 사용자 터미널을 조작하는 사건이 발생했습니다. 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"    # 행 지우기 + 행 시작 (프롬프트용)

참고자료

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 표준: 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 명령이 이를 사용.
다음 학습: