1. 先拆问题:Windows 本地剪贴板、SSH 会话和远端 tmux buffer 的三层隔离
我先描述一个很多人都会遇到的画面:你在 Windows Terminal 里通过 SSH 连到一台 Linux 开发机,开了 tmux,左右分屏。左侧 pane 跑着服务,刷出一堆日志;右侧 pane 开着编辑器。你想把左侧 pane 里某一段报错信息复制出来发给同事,于是习惯性按下 Ctrl+C——没有任何反应,再试 Ctrl+Shift+C,把整个终端屏幕都截进去了,右侧 pane 的内容也混在里面。
为什么在 tmux 里“复制单侧内容”这么别扭?因为这里其实存在三层互相隔离的东西:远端 tmux 自己的粘贴缓冲区、SSH 会话传输的终端字符流、以及本地 Windows 的系统剪贴板。你在 tmux 里按 prefix + [ 进入复制模式、按 Enter 复制的内容,默认只进入 tmux 内部的 buffer,它和 Windows 剪贴板没有任何关系;而你用鼠标在 Windows Terminal 里拖拽选中的,是终端窗口整个可见区域的字符矩形,tmux 的“左右分屏”在这个层面只是一堆画出来的线条和空格,终端根本不知道哪一列属于哪个 pane。
这也就解释了为什么左右分屏场景特别难搞:右侧 pane 的存在,导致你无论用鼠标拖选还是用 tmux 默认的行选择,都会把相邻 pane 的内容一起框进来。pane 分隔线、行尾空白、左右 pane 行数不一致,这些全是复制结果的“污染源”。如果你只是临时复制一小段,忍一忍还能在粘贴后手动删,但当你需要复制整块日志、一段带缩进的代码,或者当前屏幕放不下的历史内容时,这种粗糙操作会让人非常烦躁。
所以这篇文章我打算把 Windows/SSH 场景下复制 tmux 单侧内容的思路完整捋一遍,从鼠标原生选择到 tmux copy-mode 矩形选择,再到 capture-pane 精准导出,最后聊聊剪贴板桥接和常见坑。你可以直接照着自己的习惯选一种,不用每个都背下来。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 第一套方案:Windows Terminal 的 Shift 拖拽与 Alt 矩形选择
如果你的需求只是“把当前屏幕上左侧 pane 的这一小段文字复制出去”,最直接的办法不是进 tmux 命令,而是用终端自己的文本选择能力。在 Windows Terminal 里,默认的鼠标拖拽就是选择文本,直接把内容送进 Windows 剪贴板,省去所有中间环节。但这个方案有一个先决条件,很多新手会卡在这里:如果你在 tmux 里打开了 set -g mouse on,那么鼠标事件会被 tmux 接管,直接拖拽并不是在选择文本,而是让 tmux 选中 pane、调整分隔条或者进入 copy-mode。
解决办法是按住 Shift 再拖拽。Shift 会让终端模拟器暂时绕过 tmux 的鼠标捕获,回到最原始的原生文本选择模式。我自己的习惯是:
- 按住
Shift,从目标起点拖到终点,松开鼠标,内容已经在 Windows 剪贴板,直接 Ctrl+V 就能粘贴。 - 如果只想复制左侧 pane 的某一列区域,不把右侧 pane 的内容圈进来,可以使用 Windows Terminal 的矩形选择:在按住
Shift的同时再按住Alt,然后拖拽。鼠标拖动出来的就是一个矩形块,这样你可以精确地把选择范围控制在 pane 分隔线左侧。
为什么这个方案适合“偶尔复制一次”?因为它不需要任何 tmux 配置,零学习成本,所见即所得,复制出来的内容还自动进入本地剪贴板,完全符合 Windows 用户的操作直觉。但它有一个很难解决的毛病:矩形选择会把选择范围内所有字符都带走,包括 pane 分隔线字符、左右 pane 之间的空白列,以及左侧 pane 本身行尾的空格。如果左侧 pane 的宽度只有 80 列,而右侧 pane 的内容长得多,你按矩形选完,粘贴出来每一行都会拖着一串空格,甚至带上分隔线附近的边框字符。这些脏字符在短信、IM 工具里尤为扎眼。
所以我一般只在两种情况下用这种方案:一是内容很短,粘贴后手动删一下没问题;二是临时需要把“看到的东西”原样分享给别人,连高亮和布局一起分享,那反而需要这种“屏幕截图式”的复制。如果你追求的是干净、可编辑、可以直接进代码仓库的文本,那还是往下看 tmux 自己的方案。
3. 第二套方案:进入 tmux copy-mode,用矩形选择精确圈出左侧 pane
tmux 的复制模式天然支持“矩形选择”,这是解决左右分屏复制单侧内容最硬核、最可控的方式。核心思路是:让 tmux 把 pane 当作一个独立的文本区域来处理,由你在其中选择矩形范围,而不是让终端按整屏字符流来处理。
3.1 先把复制模式切成 vi 风格
我强烈建议在 tmux 配置里加上这一句:
code复制set -g mode-keys vi
tmux 默认的复制模式是 emacs 风格的键位,C-space 开始选择、C-w 复制之类的,说实话和大多数人的肌肉记忆不符。切到 vi 风格后,进入复制模式的光标移动就变成了 h/j/k/l,开始选择是 空格 或者 v,复制是 Enter,取消是 q。这套键位如果你用过 Vim,几乎不需要额外记忆。
配置生效后,prefix + [ 进入复制模式,你会看到光标变成一个可在 pane 内自由移动的方块。注意,tmux 的复制模式只在你当前所在的 pane 内容范围内移动,所以如果你停留在左侧 pane 并按 prefix + [,光标天然就在左侧 pane 内,不会跑到右侧去。这一点非常重要,它就是“单侧复制”的天然边界。
3.2 矩形选择的关键操作
进入复制模式后,我们常用的几个动作是这样:
h/j/k/l:移动光标到目标起点。空格或v:开始选择。C-v:切换到矩形选择模式。这是整件事的重中之重。tmux 3.x 的 copy-mode-vi 默认把C-v绑定到了rectangle-toggle,按下之后,光标移动时的选择范围会变成一个矩形块,而不是整行整行地选。- 继续用
h/l调整矩形的左右边界,用j/k调整上下边界,直到矩形刚好框住左侧 pane 的目标区域。 Enter:复制并退出复制模式。
举个例子,左侧 pane 宽度是 80 列,内容是一个多行 JSON。你从第 2 行第 5 列开始,按 空格,再按 C-v 切矩形,移到第 20 行第 78 列结束,回车。这时候选中的就是 80 列宽、19 行高的矩形块,右侧 pane 的内容一行都进不来。
对比一下默认的行选择模式:如果不按 C-v,tmux 选择的是从起点到终点经过的所有整行内容,而行是 pane 里的逻辑行,并不是终端屏幕上的物理行。在左右分屏里,选左侧 pane 的某几行,复制出来的只有左侧 pane 的实际行内容——这点乍一看是优势,但它无法只选“一行里的前半部分”,矩形选择才是真正的“按列圈地”工具。
3.3 复制进 tmux buffer 之后,怎么落到 Windows 剪贴板
这里必须诚实地说:copy-mode 复制的内容,默认只进 tmux 的 buffer,和 Windows 剪贴板没关系。你按完 Enter 后,prefix + ] 是粘贴到 tmux 内部的另一个 pane,而不是粘贴到本地聊天框。很多教程到这一步就停了,导致用户复制完发现外面拿不到。
要让内容真正进入 Windows 剪贴板,有两个常见途径。第一个是手动导出 buffer:tmux list-buffers 查看、tmux show-buffer 查看内容、tmux save-buffer - 把内容输出到 stdout。在本地 Windows 环境(Git Bash/WSL)里,你可以直接管道给 clip.exe:tmux save-buffer - | clip.exe。但注意,如果你是在 SSH 连接到的远程 Linux 上操作,远程机器上根本没有 clip.exe,这条路就断了。第二个是配置 OSC52 桥接,让 tmux 在复制时直接把内容推给终端,再由 Windows Terminal 写入本地剪贴板。这个我会在第 5 节详细讲,因为它是目前“SSH 到远端 + tmux 复制”场景下最优雅的方案。
在没配置 OSC52 之前,copy-mode 矩形选择的价值主要体现在“复制到 tmux 内部”或者“先复制再集中导出”。比如你想把左侧 pane 的内容重新组合、粘贴到右侧 pane 的编辑器里,那么这套流程非常顺滑,不受 SSH 环境限制。
4. 第三套方案:用 capture-pane 把“单侧内容”直接导出成文本
如果说 copy-mode 是“人肉选择”,那 capture-pane 就是“直接把整个 pane 的内容倒出来”。它不走屏幕帧,不涉及 pane 分隔线,读取的就是某个 pane 自己的缓冲区内容。对于“我要把左侧 pane 当前显示的全部内容,或者过去几百行历史日志,原样保存下来”这种需求,它比任何鼠标操作都干净。
4.1 先确认你要的 pane 编号
tmux 里每个 pane 都有两种标识:窗口内的 pane 索引和全局 pane id。在左右分屏里,左侧通常对应索引 0,但如果你嵌套过 pane、切换过布局,这个数字不一定是直觉上的“左 0 右 1”。最稳妥的办法是查一下:
code复制tmux list-panes -F '#{pane_id} #{pane_index} #{pane_width}x#{pane_height} #{pane_current_command}'
输出结果类似:
code复制%0 0 80x24 bash
%1 1 80x24 vim
在 capture-pane -t 参数里,你可以用索引 0,也可以用全局 id %0。我建议平时用 %0 这种写法,因为它在多窗口、多会话环境下也不会产生歧义。
4.2 捕获命令的完整用法
最基础的用法是捕捉当前 pane 的可见内容并打印到 stdout:
code复制tmux capture-pane -p -t %0
-p 表示 print,也就是直接输出到终端,而不是存进 tmux buffer。默认只抓取当前屏幕可见的行,如果你想带历史,加 -S 参数指定起始行:
code复制tmux capture-pane -p -S -200 -t %0
-S -200 的含义是“从当前可见区域往上 200 行开始抓”,一直抓到屏幕底部。如果填 -S -,表示从 pane 的历史起点开始抓,整个回滚缓冲区全部输出。日志很长的时候我不建议直接 -S -,因为你会被输出淹没,建议配合 > 文件 重定向:
code复制tmux capture-pane -p -S -500 -t %0 > /tmp/left_pane.log
如果想连转义颜色一起保留,加 -e;如果想让被折行的长行重新连接成完整逻辑行,加 -J。这两个选项要看场景:-e 适合你要保留高亮的场景,但粘贴到普通编辑器会看到一堆 [01;32m 这类 ANSI 码,所以我默认不加;-J 对代码和日志非常有用,它能把因为终端宽度而被拆成多行的长行拼回原来的完整逻辑行。
4.3 远程 SSH 场景下,怎么把导出内容弄回 Windows
这是最容易被忽略的环节。你通过 SSH 连到远端,在远端 shell 里执行 tmux capture-pane -p -t %0 > /tmp/left.log,文件写在远端服务器上,Windows 本地当然看不到。想把它弄回本地,常见做法有三种:
- 用支持 SFTP 的编辑器,如 VS Code 的 Remote-SSH 插件,直接在远端打开
/tmp/left.log,然后手动复制到本地。 - 在本地 Windows 上再开一个终端,用
scp把文件拉下来:scp user@host:/tmp/left.log .。 - 如果你只需要几十行内容,直接让远端执行
tmux capture-pane -pS -100 -t %0,把输出扔在远端终端里,然后按住 Shift + Alt 拖拽矩形选择终端内容,再粘到本地。这样虽然还是过了一道终端屏幕,但因为内容已经是“纯左侧 pane 的文本”,没有分隔线和右侧 pane 干扰,选择起来干净得多。
另外,如果你想持续记录某个 pane 的所有输出,而不是只抓一次,可以用 pipe-pane:
code复制tmux pipe-pane -o -t %0 'cat > /tmp/left_pane.log'
这个命令会把 pane 后续的所有输出实时写进文件,适合记录服务日志,跑完再用 tmux pipe-pane -o -t %0 关闭。它和 capture-pane 互补:一个是“事后按需抓”,一个是“平时持续记”。
5. 让复制更顺滑的底层配置:mouse、mode-keys、set-clipboard 与 OSC52
如果你打算长期在 Windows/SSH 环境用 tmux,我建议把下面这套配置写进 ~/.tmux.conf,它解决的是“复制到 tmux buffer 之后怎么自动进 Windows 剪贴板”这个终极问题:
code复制set -g mode-keys vi
set -g mouse on
set -g set-clipboard on
set -ga terminal-overrides ',*:Ms=\E]52;%p1%s;%p2%s\007'
逐条解释一下。
mode-keys vi 前面已经说过,是复制模式的键位基础。
mouse on 是很多人记住的第一条 tmux 配置,它让滚轮、选中 pane、拖动分隔条都变得直观。但它也会让鼠标拖拽不再直接选择文本,所以只要你开了 mouse on,就需要接受“在 tmux 里想用终端原生选择必须按 Shift”这个设定。有人觉得这很烦,但和它带来的便利比起来,我觉得完全值得。
set-clipboard on 和那条 terminal-overrides 放在一起,才是 SSH 场景的关键。它背后的机制叫 OSC 52,是终端世界里一种“让终端里的程序把内容写进终端所处系统剪贴板”的转义序列。简单理解:tmux 复制完一段文本后,会把这段文本编码进一条终端控制序列发出去,如果 Windows Terminal 支持这个序列,它就会把这串内容写入本地 Windows 剪贴板。这样你按完 Enter,内容已经躺在系统剪贴板里,直接在 Windows 侧 Ctrl+V 就能粘贴。
为什么要有那条 terminal-overrides?因为 tmux 需要知道终端支持 OSC 52,才能在复制时发送对应的转义序列。有些终端在 terminfo 里没有声明 Ms 能力,tmux 就不会自动启用。手动 override 一下,把 Ms 序列补上,等于告诉 tmux“放心发 OSC 52”。较新版本的 Windows Terminal 原生支持 OSC 52,不少情况下不用 override 也能工作;但如果你测试发现复制后剪贴板没反应,加上这条基本能解决。注意这条 override 里的写法是固定的,不要自己改动转义格式,否则可能发送错误序列。
如果你不想手写这些底层配置,也可以考虑 tmux 插件 tmux-yank,它会帮你处理系统剪贴板的同步,本质上也依赖 OSC 52 或者本地的 win32yank、clip.exe 这类工具。但插件只解决“复制出来后放哪”,解决不了“左右分屏选哪一块”的选择精度问题。所以核心的矩形选择能力,你还是需要在配置文件里靠 mode-keys vi 和 copy-mode 键位保证。顺便可以显式绑定一下矩形选择键,防止某些 tmux 版本默认键位不同:
code复制bind-key -T copy-mode-vi C-v send -X rectangle-toggle
这样即使默认绑定被覆盖,你也能保证 C-v 随时可以切换矩形选择。
这套组合配置好之后,最理想的复制流程就变成了:prefix + [、移动光标、按空格开始选择、按 C-v 切矩形、移动边界、按 Enter,然后到 Windows 里 Ctrl+V。全程不碰鼠标,内容自动进系统剪贴板,干净且高效。
6. 实测中容易翻车的几个坑
配置和方案看着都简单,但实际操作中我踩过不少坑,这里按出现频率排序说一下,免得你走弯路。
第一个坑:set -g mouse on 后,很多人发现鼠标拖拽“失效”了。其实不是失效,是 tmux 接管了鼠标。如果你此时用鼠标拖选,tmux 会认为你想选中一个 pane 或者开始 copy-mode 选择,而不是把文本送进 Windows 剪贴板。解决办法就是前面说的:按住 Shift 拖拽,让 Windows Terminal 绕过 tmux 直接做原生选择。如果你发现 Shift 也没用,检查一下 Windows Terminal 设置里是否把 Shift 键位改成了其他功能,或者你当前用的是老版本 WSL 终端。
第二个坑:矩形选择复制出来的内容带大量行尾空格。原因很简单,矩形选择是“画格子”,只要格子落在 pane 的空白列里,这些空格就会被复制。左侧 pane 如果各行的长度参差不齐,粘贴后会出现右侧一片空格。这没有百分之百的完美解法,我的习惯是:长度差异不大就粘贴后统一 trim trailing whitespace;差异很大就先 zoom 当前 pane(prefix + z),把左侧 pane 临时放成全屏,此时矩形选择的范围就只剩一个 pane,不会拖到右侧内容,复制完再按一次 prefix + z 还原布局。zoom 是解决“单侧复制”最暴力的思路,但也确实是最稳定的思路,因为它直接把另一侧暂时藏起来了。
第三个坑:capture-pane -e 复制出来一堆 [0;32m 颜色码。这是加了 -e 的副作用。-e 的作用是保留原始转义序列,适合你确实需要保留颜色的场景,但如果你只是想拿纯文本,千万别加。同理,如果你从某些终端复制带格式文本,粘贴到纯文本框里也可能看到颜色码,这是终端自身“复制包含格式”的选项在作怪,和 tmux 无关。
第四个坑:用 capture-pane -t 0 结果抓错了 pane。0 是 pane 索引,但索引会随着分屏次数和布局变化而变,并不永远等于“左边的 pane”。如果你在一个多窗口会话里操作,更靠谱的是 -t %0 这种全局 pane id,或者先用 tmux list-panes 确认编号再抓。
第五个坑:OSC 52 配置了,但复制后 Windows 剪贴板纹丝不动。先别怀疑配置,先确认终端支不支持。老的 PuTTY 是不支持的,Windows Terminal 需要较新版本才完整支持 OSC 52。其次确认你复制用的是 tmux copy-mode,而不是终端原生选择——原生选择走的是终端自己的剪贴板逻辑,不会经过 tmux 的 set-clipboard。最后,检查一下 terminal-overrides 是否真的加载了,可以用 tmux show -g terminal-overrides 查看当前值。注意 override 的 Ms 行写错一个转义符都可能导致失效。
第六个坑:复制到 tmux buffer 后,prefix + ] 粘贴时发现粘贴的是“上一次”的内容。这是因为 tmux 有多个 buffer,新复制的内容不一定排在 list 第一个位置。如果你每次都是新复制就粘贴,这个问题很少出现;但如果反复复制了多次,建议用 tmux choose-buffer 交互式选 buffer,或者 tmux list-buffers 先看一眼编号。
第七个坑:SSH 断线重连后,发现之前 tmux 里的 pane 内容还在,但复制模式选择状态没了。这其实是正常现象,tmux 会话还挂在远端,缓冲区都在,你重新 tmux attach 后,prefix + [ 还能看到历史内容,之前的 copy-mode 状态本来就不会保存。很多人最初用 tmux 就是为了“断开 SSH 后 node 服务不停”,这一点它做得很稳;但要注意,如果你断开前在 copy-mode 里选了内容但没复制,重连后需要重新选。
7. 我目前最顺手的四步工作流
最后分享一套我实际每天在用的组合打法,按内容量分四种情况,不用纠结哪种最“优雅”,按需切换就行。
第一,临时复制屏幕上的一小段:按住 Shift,直接在 Windows Terminal 里拖选,粘出去。如果在 tmux 里开了 mouse on,记得必须先按住 Shift,否则选出来的是 pane 而不是文本。要只选左侧列区域,就再加 Alt 切矩形选择。这种方式的优点是快,缺点是可能有空格和分隔线,所以我只用于几十个字符的短内容。
第二,复制代码块或者带缩进的内容,同时需要精确避开右侧 pane:prefix + [ 进 copy-mode,移动光标,空格开始选择,C-v 切矩形选择,调整边界后回车。配置好 set-clipboard on 和 OSC 52 后,内容已经在 Windows 剪贴板,直接粘贴。这个过程熟练后十秒内搞定,比鼠标方案更干净。
第三,需要保存左侧 pane 整个屏幕或者最近几百行日志:用 tmux capture-pane -p -S -200 -t %0 > /tmp/left.log,然后通过 VS Code Remote-SSH 或者 scp 把文件拉回本地。如果你需要持续记录,就先用 pipe-pane 把输出写到文件里,事后想拿多少拿多少。
第四,当前 pane 内容太长,屏幕和回滚区都放不下,或者我需要同时从左中右多个 pane 里拼一段内容:先 prefix + q 查看 pane 编号,分别 capture-pane 导出,再用本地编辑器合并。这种方法看起来繁琐,但它是唯一不会因为终端宽度、折行、颜色码而损失信息的路径。
我个人这三四年用下来的体会是,复制粘贴这种“小事”在 tmux 里最忌讳的是死磕某一招。Windows/SSH 场景本来就有多个隔离层,强行让一套方案通吃所有情况,往往会在某个边缘场景翻车。我自己的配置常年保持 mode-keys vi、mouse on、set-clipboard on 三件套,矩形选择绑定成 C-v,短期复制用 Shift 拖拽,长期导出用 capture-pane。这套思路从 Windows Terminal 到 mac 终端都沿用,换机器也不会觉得手生。
最后再补充一个小技巧:如果你经常要把某个 pane 的内容复制出去,比如左侧 pane 是实时日志,可以考虑给 tmux 加一个专门的 pane 标题,用 tmux select-pane -t %0 -T "LEFT_LOG" 标记,这样在 capture-pane 之前 list-panes 扫一眼就知道该抓哪个,比靠记忆数编号稳妥多了。复制单侧内容的本质不是“选中屏幕上的字符”,而是“让 tmux 理解你要操作的是哪个 pane”——一旦理解了这一点,剩下的都是配置问题。
