跳到主要内容
ansicode

DECTABSR —— 制表位报告(`CSI 2 $ w`)

让终端以 DCS 报告形式输出当前水平制表位。补全往返链:HTS(esc-hts)设置、TBC(csi-tbc)清除、DECTABSR 查询当前集合。

字节形式

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

\\x1b[\x1b[2$w
\\033[\033[2$w
\\e[\e[2$w
ESC [ESC [ 2 $ w
hex1b 5b 32 24 77

说明

DECTABSR —— *制表位报告* —— DECRQPSR(请求陈现状态报告)的一种,请终端以 DCS 形式列出当前水平制表位。它是制表位往返链的第三只脚:

- 在当前列设置制表位:\x1bH(HTS,见 esc-hts)。 - 清除\x1b[g(当前列)/ \x1b[3g(全部)(TBC,见 csi-tbc)。 - 查询当前集合:\x1b[2$w(DECTABSR —— 本条目)。

请求形态\x1b[2$w2 是 Ps 选择子 —— 1 是 DECCIR 光标信息报告、2 是 DECTABSR)。$ 中间字节 + w 末字节同 DECRQPSR 约定。

回复形态(DECRPTAB)\x1bP2$u<col1>/<col2>/…/<colN>\x1b\\ —— DCS 帧,体为以斜杠分隔的当前制表位列号(1 起算)。示例(xterm 80 列默认 8 列制表): ``` \x1bP2$u9/17/25/33/41/49/57/65/73\x1b\\ ``` 注意 1 列处无制表位(本即光标 home),斜杠仅分隔不终止。DCS 前缀 \x1bP2$u 回显请求的 2$ 加上 u 表示「unsolicited / response」(类比 DECRQSS 的 $r)。

往返模式(TUI 会话前后保存 / 恢复终端制表状态): ``` 启动: 发 \x1b[2$w -> 收 \x1bP2$u9/17/25/...\x1b\\ 解析:以 '/' 分割体 -> [9, 17, 25, ...] 保存 stops_before 退出: 发 \x1b[3g # TBC 清全部 for col in stops_before: 发 \x1b[<col>G\x1bH # CHA 至 col,HTS 于 col ```

声明 dectabsr 的 terminfo 条目里,tput rs2 做的就是这件事 —— 「重置到已知制表状态」用读 + 还原,而不是傻发 \x1b[?5W(DECST8C —— 设 8 列制表)。

边界: - 空回复(\x1bP2$u\x1b\\)表示一个制表位都没有 —— 发 \x1b[?5W 或写一串 HTS 还原默认。 - 部分模拟器会把隐含的「行尾」位(列 == 屏宽 + 1)也报出来 —— 从保存列表里剥除。 - 回复默认以 \x1b\\(ST 表示为 ESC + 反斜杠)结束;xterm 在 8 位 C1 模式下(见 decscl-compat-level)使用单字节 \x9c

覆盖度xterm = 完整(标准)。WezTerm / mlterm / Konsole = 完整。Ghostty = 部分(回但总是默认 8 列)。Kitty / iTerm2 = 部分(回但某些代码路径漏掉用户设置的位)。Alacritty / Linux console / Windows Terminal / cmd / ConPTY / macOS Terminal / gnome-terminal = 无作用(完全不回 —— 探测务必加 ~100 ms 超时)。

规范出处: DEC VT510 RM (DECTABSR / DECRPTAB) / xterm-ctlseqs (CSI 2 $ w)

示例

bash
# Probe tab stops; read DCS reply with timeout.\nprintf '\\033[2$w'\nIFS= read -rs -d '\\\\' -t 0.1 reply </dev/tty 2>/dev/null\necho \"DECTABSR: ${reply}\"   # expect: \\x1bP2$u9/17/25/...
python
import sys, re\n# Parse the slash-separated tab list from a DECRPTAB reply.\nreply = '\\x1bP2$u9/17/25/33/41/49/57/65/73\\x1b\\\\'\nm = re.search(r'\\x1bP2\\$u([\\d/]*)\\x1b\\\\', reply)\nstops = [int(c) for c in m.group(1).split('/') if c] if m else []\nprint(stops)
go
// Save-then-restore tab stops across a TUI's runtime.\nfmt.Print(\"\\x1b[2$w\")              // request DECTABSR\n// ... read DCS reply, parse to []int ...\n// On exit: TBC all, then HTS at each saved col\nfmt.Print(\"\\x1b[3g\")               // TBC 3 clears all\nfor _, col := range saved {\n    fmt.Printf(\"\\x1b[%dG\\x1bH\", col)\n}
javascript
// Detect 'no tab stops at all' (empty body).\nfunction parseDECTABSR(buf) {\n  const m = /\\x1bP2\\$u([^\\x1b]*)\\x1b\\\\/.exec(buf);\n  if (!m) return null;\n  return m[1].split('/').filter(s => s.length).map(Number);\n}\nconsole.log(parseDECTABSR('\\x1bP2$u\\x1b\\\\'));   // [] — no tabs at all
c
/* Skeleton: probe + parse (cbreak stdin, timed read). */\nprintf(\"\\x1b[2$w\"); fflush(stdout);\n/* read until ST (0x1b 0x5c). then strtok on '/' between \"$u\" and ST. */

终端支持

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

相关序列