Skip to main content
ansicode
C0 controls (single-byte)

C0 controls — the single-byte control characters

C0 control codes. The 32 single-byte controls below 0x20 — BEL 0x07, BS 0x08, HT 0x09, LF 0x0a, CR 0x0d, ESC 0x1b, SI / SO, and friends. Not escape sequences per se, but every terminal emulator parses them alongside SGR / CSI and they show up in any ANSI-laden log.

1 sequence

When to use C0

Reach for C0 controls when you need a single-byte primitive that needs no \x1b introducer at all\b to back the cursor up, \t to advance to the next tab stop, \n to feed a line, \r to return to column 1, \a to ring the bell. These are the seven-bit control codes ASCII set aside for terminal control before ANSI / VT existed, and every terminal still acts on them inline. One byte per action (vs three+ for the smallest CSI) — that's the whole reason they're still in your printf format strings. C0 is also the substrate the other families build on: \x1b itself (ESC, 0x1b) is a C0 code, and \a (BEL, 0x07) doubles as the de-facto OSC string terminator.

Common C0 one-liners

Paste any of these into bash / zsh — printf interprets the \x1b byte.

  • Ring the bell
    printf '\a'
  • Progress bar (CR + overwrite)
    printf '\rdone: %d%%\n' 50
  • Tab-aligned two-column table
    printf 'name\tsize\nbig\t1G\n'

C0 cookbook — four single-byte primitives that don't need ESC

C0 is the family that predates the escape character itself — every code is one byte, no \x1b introducer, and every terminal from a 1970 VT05 to 2026 Ghostty acts on them inline. Four stops cover what you'll actually emit in modern code: the CR / LF pair that ends a line, BS for non-erasing cursor pullback, HT for tab-aligned columns, and BEL for the audible alert that doubles as an OSC string terminator.

  1. 1. CR + LF — the line-ending pair

    CR (\r, 0x0d) returns the cursor to column 1 of the current line; LF (\n, 0x0a) drops the cursor down one line. On a Unix tty the kernel's onlcr mode adds an implicit CR before every emitted LF, which is why a bare printf '\n' lands at column 1 of the next line in your interactive shell. Turn onlcr off (stty -onlcr) and the same \n produces a 'staircase' effect — each successive line starts further to the right because nothing reset the column. In alt-screen (\x1b[?1049h) the kernel onlcr is bypassed — render loops must emit explicit \r\n between rows or the cursor walks off the right. Same on Windows where the console expects CRLF as the literal line terminator. \r alone is the classic progress-bar pattern: printf '\r%d%%' "$pct" repeatedly overwrites the same line.

  2. 2. BS — back up one column without erasing

    Backspace (\b, 0x08) moves the cursor one column to the left and does not erase the character it lands on. Print abc, emit \b, and the cursor sits on top of the c — a subsequent x overwrites the c in place. To actually wipe a character the canonical 3-byte dance is \b \b (backspace, space, backspace) — back up, paint a blank, back up again. Modern code more often reaches for \x1b[K (EL — erase line from cursor) instead, but the \b \b pattern is what readline / GNU getline still use for terminal-echo password masking and inline edits. BS is also what the Backspace key normally sends in raw mode — some terminfo entries swap it with DEL (0x7f), and the disagreement between BS and DEL is the perennial source of the 'my Backspace beeps' Ctrl-H confusion.

  3. 3. HT — horizontal tab and the TBC / HTS lineage

    Horizontal Tab (\t, 0x09) advances the cursor to the next column whose index is a registered tab stop. The default grid is every 8 columns (1, 9, 17, 25, …), so printf 'a\tb\tc' lands a at col 1, b at col 9, c at col 17. Applications can shift this grid with HTS (\x1bH) — marks the current column as a tab stop — and TBC (\x1b[g clears the stop at the current column; \x1b[3g clears every stop). Together HT + HTS + TBC implement the 1970s typewriter-tab interface that survived into ANSI verbatim. The reverse direction is CBT (\x1b[Z) — back up one tab stop, the Shift-Tab counterpart. Hot tip: HT does not insert space characters into the buffer — it moves the cursor over existing content. If you tab past text and emit a space at the destination, you'll see that space painted, not the underlying content preserved.

  4. 4. BEL — the alert byte that doubles as the OSC terminator

    Bell (\a, 0x07) was originally the byte that rang the bell of a teletype. Modern terminals turn it into either an audible beep, a visual flash, or a notification badge — controlled by per-app preferences (xterm's bellIsUrgent, iTerm2's *Profiles → Terminal → Silence Bell*, kitty's enable_audio_bell, etc.). Its second life is much more important: BEL is one of the two standard string terminators for OSC sequences. \x1b]0;title\x07 sets the window title; the trailing \x07 closes the OSC string just as ST (\x1b\\) would. xterm and every fork accepts BEL-terminated OSC, which is why almost all CLI tools emit it instead of the ECMA-48-correct ST. The cost: if your tool emits a BEL outside an OSC envelope while the user has their terminal set to play the bell, you'll get an unexpected ding. Defensive tip: many emulators (Konsole, gnome-terminal, kitty) suppress BEL inside OSC envelopes specifically — silence in OSC, ring outside — but the legacy Linux console rings either way. If your CLI emits both visible diagnostics and OSC titles, test under xterm -e ./your-cli with the audible bell enabled to be sure.

All sequences in this family

Browse other families