CSI Control Sequence Introducer
Begin a control sequence: parameters, intermediates and a final byte.
CSI Pm Pi Pf
- ECMA-48
- §8.3.16 CSI – Control Sequence Introducer, p. 36
- DEC STD 070
- §3.5.3 Control Sequences, p. 3-23
- VT510
- 4.3.3 Control Sequences
CSI does nothing by itself. It starts a control sequence, and most of the sequences on this site are control sequences. ECMA-48 gives the format in §5.4, p. 10:
| Part | Bytes | What it is |
|---|---|---|
CSI | ESC [ | the introducer |
P…P | 0–9 : ; < = > ? | parameter bytes, if any |
I…I | space and ! to / | intermediate bytes, if any |
F | @ to ~ | the final byte, which ends the sequence |
The intermediate and final bytes together name the function: CSI 2 J is ED, and CSI 2 SP q, with a space as intermediate, is something else entirely. Final bytes p to ~ are set aside for private use, which is where many of DEC’s sequences live.
Parameters. Numbers are written in decimal and separated by ;, as ECMA-48 sets out in §5.4.2, p. 12. An empty parameter stands for the function’s default: CSI ; 5 H is line 1, column 5. Leading zeros do not count, and DEC STD 070 makes a parameter of zeros alone the default too, which is why a 0 so often means 1 (§3.5.3.1, p. 3-24). Trailing empty parameters may be left out, separators and all. A value too large to hold should be taken as the largest the terminal supports.
Private parameters. A parameter string that starts with <, =, > or ? is private: its meaning is not standardized, and CSI ? 6 h (DECOM) and CSI 6 h are different functions. DEC STD 070 says one of those bytes anywhere else makes the whole sequence invalid, to be ignored up to and including its final byte (§3.5.3.4, p. 3-26). So does a parameter byte after an intermediate (§3.5.3, p. 3-23), and so does :, which DEC STD 070 reserves (§3.5.3.1, p. 3-24). SGR is the exception everyone makes now, with colons separating the parts of a color or underline style.
Ending early. A sequence ends at its final byte. CAN or SUB part-way through abandons it, and an ESC abandons it and starts another; other control characters inside one are carried out on the spot, as BEL’s page shows. A sequence the terminal does not implement is ignored as if it had not arrived.
The 8-bit form. ECMA-48 also gives CSI as the single byte 9B. A terminal reading UTF-8 does not take it as CSI: the code point U+009B is ignored, and a lone 9B byte is not valid UTF-8. A default xterm ignores that byte as well when printable text follows it, while libghostty-vt prints it as U+FFFD, so that case is a known difference. Either way, what follows is printed as text. The UTF-8 page has the details.
Validation
CSI-1: Empty and zero parameters take the default
\e[;3H # line left out
A
\e[000;002H
B
\e[3;H # column left out
C
|_BA___|
|______|
|C_____|
CSI-2: A value too large is taken as large as possible
\e[99999999;2H
X
|______|
|______|
|_X____|
CSI-3: Malformed sequences are ignored
\e[2?3H # private marker in the middle
\e[2\s3H # parameter after an intermediate
\e[2:3H # a colon
X
|X_____|
|______|
|______|
CSI-4: An unknown sequence is ignored
AB
\e[5~ # a function key code, not a control function here
C
|ABC___|
cursor 1,4
CSI-5: The 8-bit forms
\x9b # a lone byte: xterm ignores it before text
2;2H
|2;2H__|
|______|
CSI-6: U+009B is not CSI either
\u{9b} # U+009B as UTF-8: ignored
2;2H
|2;2H__|
|______|