跳到主要内容
ansicode

TBC — 清除制表位(CSI g)

清除一个或全部 tab 停位,改变 HT 和 CBT 的落点。

字节形式

涵盖所有常见的字符串字面量写法,方便正反查找。

\\x1b[\x1b[g (clear current) \x1b[3g (clear all)
\\033[\033[g / \033[3g
\\e[\e[g / \e[3g
ESC [ESC [ Ps g
hex1b 5b <Ps> 67

说明

ECMA-48 §8.3.154 —— 制表位清除(TBC)。末字节 g(0x67)。参数控制范围:0(默认,省略时同)仅清除 *当前列* 的 tab 停位;3 清除整个终端中 *所有* tab 停位。字节序列 \x1b[g(清当前)与 \x1b[3g(清全部)。与 HTS(\x1bH,见 esc-hts,在当前列设置 tab 停位)配对 —— TBC 是它的「清除」对半。默认每 8 列一个 tab 停位(9、17、25 …);每个新开终端都以此网格起步,\x1b[3g 是把白板擦干净的原语,便于 HTS 重新铺设与表单 / 表格 / 列对齐输出匹配的自定义布局。

可移植性 —— xterm、iTerm2、kitty、alacritty、wezterm、gnome-terminal、Konsole、Ghostty、Linux console、Windows Terminal、ConPTY 1809+ 通用。Windows 旧 cmd.exe(ConPTY 之前)忽略 TBC。最常见的作者意外:一旦发出 \x1b[3g,HT(\t,0x09)就 *什么也不做* —— 没有 tab 停位可推进,光标原地不动;用 \t 排版的工具(columnprexpand、Makefile 配方输出、不少 heredoc 格式化字符串)会静默输出错乱 / 左对齐的结果,直到重新设置停位。防御性写法:总是把 \x1b[3g 与一串 HTS 配对发送,再把控制权交还用户;切勿留下「停位全清、未设新位」的状态 —— 这是 TUI 崩溃后特别恶心的状态泄漏。terminfo tbc cap 存在,但终端条目很少填充;程序通常直接发字节。

清除状态在正常屏幕操作下持久存在 —— SGR 0、清屏(\x1b[2J)、Ctrl+L 换页(\x0c)甚至全新提示符都 *不会* 恢复默认 8 列网格。只有 DECSTR(\x1b[!p,见 decstr-soft-reset)、RIS(\x1bc,见 ris-reset)或另一次 TBC + HTS 序列能撤销。tmux 把内部 pseudo-terminal 的 tab 停位规范化为默认 8 列网格,因此 pane 内发出的 TBC 在多路复用器边界被截留而非转发到外层终端 —— 与 HTS 同形态,同一对警告也适用(重新接入会话时,tmux 感知的代码应重新发出期望的 tab 布局)。GNU screen 行为相同。相关:esc-hts(HTS —— 配套的「在此设置 tab 停位」一半)、csi-cbt(CBT —— \x1b[<N>Z 按停位后退 N 次,CHT 的对称对应)、c0-controls(C0 表,含 HT \x09 —— TBC 所控制的字面 tab 字符)。

规范出处: ECMA-48 §8.3.154 (TBC)

参数

0清除当前列上的 tab 停位
3清除所有 tab 停位

示例

bash
printf '\033[3g'      # clear all tab stops\nprintf '\033H'        # set a tab stop here (HTS, ESC H)
python
import sys; sys.stdout.write('\x1b[3g')
go
fmt.Print("\x1b[3g")
javascript
process.stdout.write('\x1b[3g')
c
printf("\x1b[3g");

在哪里用到

实际会发出该序列的工具——把抽象字节锚定到你已经用过的命令上。

  • vim / neovim `:set tabstop=N`缓冲区中途改 `tabstop` 会触发 `\x1b[3g`(TBC 模式 3 —— 清所有 tab 停止位),随后在每个新停止列发 HTS(`\x1bH`)—— `:redir @a` 在 `:set tabstop=4` 切到 `:set tabstop=8` 时可捕获
  • GNU coreutils `expand` / `unexpand`写到真 TTY(vs 管道输出)的实现在转换前发 TBC 重置停止位,使 `expand --tabs=4 file` 在不同终端默认 tab 停止位(部分 VT 默认每 8、其他每 10)间保持确定性
  • ed(1) `T` command (set tab)BSD ed 与 FreeBSD ed 通过发 `\x1b[g`(TBC 模式 0 —— 清当前列停止位)再重定位来实现历史 `T` 命令 —— 为 POSIX.1-2024 兼容性保留,现代 shell 中极少调用
  • terminfo `tbc` capability (clear_all_tabs)在 `xterm-256color`、`linux`、`screen-256color` 等 ~280 个 terminfo 条目中,`tbc` 能力硬编码为 `\E[3g` —— `tput tbc` 解析为相同字节,被 initscripts 与 `tset` 用于登录时统一 tab 停止位
  • Emacs `tab-width` buffer-local recalc缓冲区局部 `tab-width` 经 `set-variable RET tab-width RET` 修改时,重显引擎发出 TBC + HTS 对以对齐新停止位 —— `M-x untabify` 完成区域转换后也用同一模式确保后续 tab 按键对齐

常见问题

针对这条序列,开发者真正会去搜索的问题的简短回答。

\x1b[g\x1b[3g 有什么不同?
\x1b[g(省参,等同 \x1b[0g)只清除*当前光标列*的 tab 停位 —— 终端中其它停位不动。\x1b[3g 清*全部*停位。无中间档:ECMA-48 §8.3.154 通过参数 03 仅定义两种范围,其它值保留且多数终端忽略。实际影响:CUP 定位到第 9 列后发 \x1b[g 只删除第 9 列的停位,17 / 25 / 33 / … 仍在,HT 仍按预期推进。\x1b[3g 抹白板,之后 HT(\t)会失活,直到通过 HTS(\x1bH,见 esc-hts)铺新停位。
\x1b[3g 之后如何恢复默认 8 列的 tab 网格?
三种方案,破坏面递增。(1) 在每个期望列重发 HTS:CUP 到第 9 列,\x1bH;CUP 到第 17 列,\x1bH;重复到终端宽度。这是唯一外科级方案 —— 不动其它 DEC 状态。(2) DECSTR 软复位(\x1b[!p,见 decstr-soft-reset)—— 同时恢复光标可见性、origin 模式、autowrap 及多数 DEC 私有模式与 tab 停位;TUI 会话间安全使用,但会重置更多东西。(3) RIS 硬复位(\x1bc,见 ris-reset)—— 还会清回滚与 SGR;应用内通常太激进。注意:清屏(\x1b[2J)、换页(\x0c)、SGR 0 都*不会*恢复 tab 停位 —— 这是 TBC 最常见的作者陷阱。

终端支持

xterm
支持
Linux console (fbcon)
支持
macOS Terminal.app
支持
iTerm2
支持
Windows Terminal
支持
cmd.exe / ConPTY
部分
kitty
支持
alacritty
支持
WezTerm
支持
Ghostty
支持
GNOME Terminal
支持
Konsole
支持
tmux
支持
GNU screen
支持

相关序列

在家族食谱中

ESC 食谱 · 2. 制表位 —— `\x1bH`(HTS)设、`\x1b[g`(TBC)清