DECSET ?2004 — 括号粘贴模式
将粘贴文本用独立的转义标记包裹,让应用区分粘贴与键入。
字节形式
涵盖所有常见的字符串字面量写法,方便正反查找。
\x1b[?2004h (enable) \x1b[?2004l (disable)\033[?2004h / \033[?2004l\e[?2004h / \e[?2004lESC [ ? 2 0 0 4 h / l1b 5b 3f 32 30 30 34 68 / 6c说明
xterm-ctlseqs 记录为 DEC 私有模式 2004(2005 年加入 xterm patch #203,现已是普遍默认)。启用字节序列为 \x1b[?2004h,关闭为 \x1b[?2004l —— 都是标准 DEC 私有模式开关,前导 ? 把它们与常规 CSI 参数区分。启用后,*终端*(而非应用)会把每次发给前台程序的剪贴板粘贴用 \x1b[200~ 紧靠粘贴字节前、\x1b[201~ 紧靠粘贴字节后包起来。应用据此区分「用户键入的」与「用户粘贴的」—— 这对 shell 至关重要(粘贴中途不会触发键盘快捷键 / 自动缩进 / 历史展开),对编辑器同样关键(粘贴会作为单一 undo 单元而非逐键)。这两个标记本身是 xterm 扩展:ECMA-48 没定义,也没有其他转义在该位置使用 ~ 末字节。
GUI 终端支持面广(xterm、iTerm2、kitty、alacritty、wezterm、gnome-terminal、Konsole、Ghostty、Windows Terminal、ConPTY 1809+ 通过 Win32 console-mode 位 ENABLE_VIRTUAL_TERMINAL_INPUT),但 Linux console / fbcon 不支持(内核 pty 没有剪贴板概念)。用户最常反映的 bug 是 stuck-bracketed-paste:TUI 启动时发了 ?2004h 但退出前崩溃 / 被杀,没来得及发 ?2004l,终端仍处在括号粘贴模式,用户在普通 shell 中粘贴变成提示行上的字面字符串 ^[[200~ls -la^[[201~。恢复办法:printf '\x1b[?2004l' 或 reset。bash 5.1+ 与 zsh 5.8+ 的 readline 实际*理解*这两个标记,绑定正确时会静默吃掉 —— bind -p | grep paste 检查绑定;较老的 shell 或没有 readline 绑定的 shell 会让标记泄漏。tmux 透传该转义并在外层叠加自己的粘贴包裹(tmux 3.2+);GNU screen 4.x / 5.0 也正确转发括号粘贴。这对转义没有 terminfo cap —— 能力探测只能靠终端版本探针(\x1b[>q DA2)或试错。
配对关闭 \x1b[?2004l 在退出前必须发。健壮模式是把启用与关闭放在同一个 atexit / 信号处理块里,与其他终端模式清理(rmkx 关应用键盘、光标恢复、关鼠标、退出 alt-screen)并列。更耐久的做法是 XTSAVE / XTRESTORE 栈(\x1b[?2004s 把先前状态入栈、\x1b[?2004r 弹回),让终端回到父 shell 期望的状态 —— 这点很重要:父 shell 自己若绑了括号粘贴,子进程退出后会把模式再开回来,无条件关掉反而错;但 XTSAVE / XTRESTORE 不平衡又会朝反方向制造混乱。在粘贴*安全*层面 —— 防止粘贴注入(用户不慎粘进攻击者构造的 shell 命令)—— 括号粘贴是协议层;shell 绑定再决定要么直接插入(zsh / bash 5.1+ 默认)要么拒绝并提示(更保守)。相关:alt-screen(DECSET ?1049 —— TUI 启动通常与 ?2004h 一并发)、cursor-visibility(?25 —— 同一清理块)、dec-focus-events(?1004 —— 同一清理块)。
规范出处: xterm-ctlseqs (Private mode 2004)
示例
printf '\033[?2004h' # enable\nprintf '\033[?2004l' # disableimport sys; sys.stdout.write('\x1b[?2004h')fmt.Print("\x1b[?2004h")process.stdout.write('\x1b[?2004h')printf("\x1b[?2004h");在哪里用到
实际会发出该序列的工具——把抽象字节锚定到你已经用过的命令上。
- bash 4.4+ readline开启 `?2004h`,使粘贴文本作为整块送达,而不是被当成按键序列(如 Enter)
- zsh`bracketed-paste-magic` ZLE 部件——粘贴期间禁用展开和历史扩展
- vim, neovim自动检测 `xterm-bracketed-paste`——粘贴自动进入插入模式,且不触发缩进 / abbreviation
- fish — bracketed-paste binding shipped by default
- psql, sqlite3 readline-backed CLIs防止粘贴的 SQL 块在每个内嵌换行符处自动执行
常见问题
针对这条序列,开发者真正会去搜索的问题的简短回答。
- 为什么我粘贴时终端里出现
[200~? - 你的 shell 或 TUI 开了括号化粘贴(
\x1b[?2004h)却没关(\x1b[?2004l)就退出。终端仍在每次粘贴前后包裹\x1b[200~…\x1b[201~标记,但没人消费——它们就以字面文本落到提示符里。运行printf '\033[?2004l'关闭,或打开新终端。参见stuck-bracketed-paste误区条目。 - 为什么需要括号化粘贴?
- 没有它的话,向 shell 粘贴多行内容与用户逐行输入没法区分——每个
\n都会立刻触发命令执行,粘贴代码片段会把每行中间结果都当命令跑。括号化粘贴告诉 shell「这是粘贴不是输入」,让它能缓冲整个块、延后回车 / 历史展开,并防止针对sudo的粘贴注入攻击。
终端支持
- xterm
- 支持
- Linux console (fbcon)
- 不支持
- macOS Terminal.app
- 支持
- iTerm2
- 支持
- Windows Terminal
- 支持
- cmd.exe / ConPTY
- 支持
- kitty
- 支持
- alacritty
- 支持
- WezTerm
- 支持
- Ghostty
- 支持
- GNOME Terminal
- 支持
- Konsole
- 支持
- tmux
- 支持
- GNU screen
- 支持
| xterm | Linux console (fbcon) | macOS Terminal.app | iTerm2 | Windows Terminal | cmd.exe / ConPTY | kitty | alacritty | WezTerm | Ghostty | GNOME Terminal | Konsole | tmux | GNU screen |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 支持 | 不支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |