
平时用 tmux 的人多多少少都会遇到这种场景窗格里跑完一个大任务满屏的输出想原样保存下来发给同事参考或者在自己的本地剪贴板里留一份。用鼠标直接选中吧轻则断行重则滚轮一划选中的内容全没了想从日志文件里重新截取又要先去翻路径、定位行号费时费劲。这套“tmux 窗格内容全选输出到文件/本地剪贴板”的操作我折腾过好几回从 copy mode 到 pipe 命令最后定下来的方案其实很简单让 tmux 自己把当前窗格的缓存内容整体交出来用管道喂给文件重定向或系统剪贴板工具。这篇文章就是从我的自用配置里整理出来的把原理、快捷键、脚本和踩过的坑一次讲完。这些命令在 tmux 3.x 下都实测过。无论你是把 tmux 当终端管理器重度使用还是刚接触它的新手只要遇到“想把屏幕上这一大块内容干干净净复制出来”的需求下面的内容都能直接用。1. 先理清楚需求再选方案1.1 我们到底想从窗格里拿到什么很多人第一次想到的解决方案是用鼠标从终端里复制。在普通终端里这么做没问题但在 tmux 里会碰到几个坎第一tmux 窗口里的内容不但是在内存中渲染的还分管历史和当前可见区。屏幕只是“窗口”真正的内容在 tmux 的 buffer 和 history 里。用鼠标直接拖选拖的是终端渲染结果长行会被折行截断复制出来贴到别处每行都带着终端宽度的“折行痕迹”。第二鼠标选择对“全部内容”这个需求不友好。窗格里跑完构建、测试、大段日志输出可能向上滚动了好几千行光靠鼠标拖选很容易漏掉中间部分也特别费眼睛。第三我希望管线化。复制出来的文本最好能直接进入管道既能写到文件里又能送给剪贴板工具还能在保存前做一次清洗比如去掉 ANSI 颜色码。鼠标复制做不到这一点。所以从这里开始我一直坚持“先用 tmux 自己的命令把内容接管过来再决定去向”的思路。核心需求其实就是三句话能拿到全量历史内容、能保留原始换行和格式、能通过管道或重定向落盘/进剪贴板。1.2 几条常走的路线对比想要把窗格内容弄出来网上能看到的方法大概有四类。我按“适合我的程度”排个序放在下面这张表里。方案思路优点缺点适用场景copy mode 鼠标选择进入复制模式后用鼠标/键盘选中yank 进 tmux buffer操作直观适合小段文字大段内容容易选错折行处理靠手工随手复制一两行copy mode buffer 中转复制进 tmux buffer 后再用 save-buffer 导出到文件灵活tmux buffer 不怕会话中断操作链路长无法一键拿到整窗格需要先筛选内容再导出capture-pane 管道将窗格内容直接输出到 stdout配合重定向或剪贴板工具稳定可靠适合全量抓取可完全自动化需要记参数绑快捷键需要配置日常归档、分享、日志采集set-clipboard OSC52通过终端转义序列把 tmux 复制内容同步进系统剪贴板复制后全自动无需额外工具依赖终端支持不是所有终端都可用日常快速复制我现在的使用习惯是随手复制小段文字就走 copy mode 或 OSC52整窗格保存或发给别人一律走 capture-pane 管道。为什么最后会选 capture-pane因为它根本不经过“选中”这个步骤tmux 的窗格缓存里有多少行它就输出多少行只要历史缓冲区够大就能拿到完整内容。而且它天然就是文本流接文件、接剪贴板、接其他命令都特别顺手。2. copy mode 和 buffer理解基础才能走得更远2.1 copy mode 的基本操作在深入 capture-pane 之前还是先把 tmux 最基本的“复制”路径走一遍。毕竟 capture-pane 能抓全量但如果你想从一大段历史里挑出中间一小块copy mode 仍然是最快的。默认情况下按前缀键通常是Ctrl-b再按[进入 copy mode。进入之后可以用方向键或PageUp、PageDown翻看历史。选中内容的方式取决于你启没启用 vi 模式。我在.tmux.conf里写的是set -g mode-keys vi这样进入 copy mode 之后就能用j、k上下移动h、l左右移动按空格开始选中按回车把选中的内容复制进 tmux buffer按]在窗格中粘贴 buffer 里的内容。这里有个容易忽略的地方复制完成后内容并不是直接进你的系统剪贴板。它先进的是tmux 自己的 paste buffer这是 tmux 内部管理的一块内存区域。默认情况下 buffer 和系统剪贴板是两条线并不会自动互通。很多新手以为按了回车就能Ctrl-v贴到浏览器里结果发现完全没反应就是因为没理解这个机制。2.2 buffer 到底能干什么tmux 的 paste buffer 其实很强大。由于它在 tmux 内部哪怕你从一个窗格复制完立刻切到另一个窗格它也不会丢。而且 buffer 可以同时保存多条历史记录用以下命令查看和管理tmux list-buffers # 列出所有 buffer tmux show-buffer # 显示最近的 buffer 内容 tmux show-buffer -b buffer-name # 显示指定 buffer tmux delete-buffer -b buffer-name # 删除指定 buffer tmux save-buffer -b buffer-name -a /path/to/file # 把指定 buffer 追加写入文件save-buffer的-a表示追加而不是覆盖这一点特别适合做“把多次复制的零散内容都汇总到一个笔记文件里”这种操作。我早期就是这么干的随手复制然后再找一个快捷键把 buffer 统一追加到当天的记录文件里。值得留意的是buffer 只是一块“中转内存”。复制完不导出关掉 tmux server 也就没了。真正要落盘还是得靠下面这条命令tmux save-buffer -a ~/tmux-pastes.log2.3 给 buffer 导出一个快捷键这里可以顺手绑一个键把当前 buffer 保存到文件。比如在.tmux.conf里加入bind b run-shell tmux save-buffer -a ~/tmux-pastes.log这样以后复制完任何内容按prefix b就会把最近的 buffer 追加进~/tmux-pastes.log。为什么这里用追加而不是覆盖因为我大多数情况下是在持续收集资料覆盖会丢掉之前的内容。如果某一天我只想要当前这一份也用命令行单独执行一次tmux save-buffer ~/xxx.txt就好。buffer 的路径只是“中转站”真正的输出方式完全看你在导出时怎么重定向。不过这套 buffer 流程有一个天然限制它保存的是你“亲手选中”的内容而不是整个窗格的完整内容。如果我想把当前窗格从历史第一行到最后一行的东西原样抠出来copy mode 就不够痛快了这时候必须请出capture-pane。3. capture-pane把整个窗格焊成文本流capture-pane是 tmux 里一个老牌命令意思是“捕获当前窗格的内容”。它可以把某个窗格的可见区、以及有历史记录的部分全部以文本形式输出甚至可以保留或去除 ANSI 颜色序列。更关键的是它支持-p参数直接输出到 stdout而不是写入 tmux buffer。这就意味着它可以直接接管道。3.1 capture-pane 的参数逐个说刚开始用 capture-pane 时我是记不住参数的。后来发现把它拆成几类就好记了一是“控制目标”二是“控制范围”三是“控制输出形式”。目标用-t指定。平时操作当前窗格可以直接写成.也可以写会话名:窗口号.窗格号。比如tmux capture-pane -t 0:1.2 -p表示捕获会话 0 的窗口 1 的窗格 2。先跑一句tmux list-panes -a能列出当前所有窗格以及它们对应的 ID方便确认目标。范围用-S和-E指定。-S是开始行-E是结束行。这里有个很重要的行为负数表示“当前可见区域上方的历史行数”正数表示从当前可见区域顶部往下数。默认不写-S和-E的话只捕获当前可见的那一屏。想拿全部历史一般就写很大的负数比如tmux capture-pane -t . -S -5000 -p这个命令的意思是从当前窗格可见区域往上数 5000 行开始捕获一直到当前可见区域底部。如果历史不足 5000 行它会从最早能拿到的行开始不会因此报错。注意能拿多少很大程度上取决于 tmux 的history-limit配置这个后面会细说。输出形式用-p、-J、-e控制。-p是输出到标准输出-J是合并折行把终端里因为宽度不够而自动换行的逻辑行还原成完整的一行-e则是保留转义序列也就是 ANSI 颜色码。我的建议很简单需要保存为纯文本、发给别人看的时候不要加-e需要保留原始高亮或精确复现格式时再加。默认不带-e输出的就是干净文本正好满足大多数归档需求。3.2 把内容写进文件一行命令说干就干把当前窗格内容存到文件里我最常用的命令是TARGET~/tmux-dumps/$(date %Y%m%d-%H%M%S).log mkdir -p ~/tmux-dumps tmux capture-pane -t . -S -3000 -J -p $TARGET先建目录再用带时间戳的文件名避开覆盖接着重定向。这里-J很关键尤其是在跑日志、构建输出的时候没有它被折行的长行会被硬生生切成好几段贴出来还得手工拼回去。-S -3000是一个平衡值历史够大又不至于让输出太大。如果你的 tmux 把history-limit调到很大这里也可以跟着调大。如果你想一次抓多个窗格可以用 for 循环for pane in $(tmux list-panes -a -F #{pane_id}); do tmux capture-pane -t $pane -S -1000 -J -p $pane.txt done这段会为每个窗格生成一个独立文件文件名就是窗格 ID。日常排查多窗格问题时会特别好用。3.3 直接把内容送给本地剪贴板文件输出虽然稳但有时候我就想立刻把内容粘贴到聊天工具里这时候就需要管道到“本地剪贴板”了。关键点在于不同系统下的剪贴板工具不一样Linux 的 X11 环境xclip或xselLinux 的 Wayland 环境wl-copymacOSpbcopyWindows / WSL通常是clip.exe或win32yank.exe最经典的三条命令# Linux以 xclip 为例 tmux capture-pane -t . -S -3000 -J -p | xclip -selection clipboard # macOS tmux capture-pane -t . -S -3000 -J -p | pbcopy # Windows 的 WSL 环境 tmux capture-pane -t . -S -3000 -J -p | clip.exexclip -selection clipboard表示写入系统剪贴板的主选区和剪贴板区域。pbcopy是 macOS 自带的。clip.exe在 WSL 里可以直接把标准输入转发给 Windows 剪贴板平时用起来很方便。这几个工具本质上都是“把 stdin 的内容放进剪贴板”的薄封装所以关键不在工具本身而在你能否把 tmux 的输出正确喂过去。一旦这条管道跑通后面做快捷键就非常顺。3.4 绑两个快捷键从此不再敲命令每次需要的时候手动敲命令还是太啰嗦了。我在.tmux.conf里加了两个绑定一个负责保存文件一个负责复制到剪贴板。bind C run-shell tmux capture-pane -t . -S -3000 -J -p ~/tmux-dumps/pane-$(date %s).log bind P run-shell tmux capture-pane -t . -S -3000 -J -p | xclip -selection clipboard大写C和P都是空闲键位不会和常见操作冲突。按prefix C当前窗格内容就会带时间戳存入~/tmux-dumps/按prefix P内容直接进系统剪贴板。如果你在 macOS 上把P那行的xclip换成pbcopy如果在 WSL 里就换成clip.exe。有个细节需要提醒run-shell里的命令是在 tmux 的子 shell 里执行的它会继承 tmux 的环境变量但不一定有你交互 shell 里的完整PATH。如果某些工具明明装了却提示找不到可以在.tmux.conf里先用setenv -g PATH把路径补全或者在命令中使用绝对路径。3.5 如何指定“不是当前窗格”的目标上面绑定的快捷键默认都是抓当前窗格。但 tmux 有一个特性你在任意窗格里按快捷键-t .永远指向“按快捷键那一刻所在窗格”。这通常没问题可如果你想要从一个后台停滞的窗格里抓内容一边看另一个窗格一边操作就得按目标来指定。先列出所有窗格tmux list-panes -a输出里会显示session:window.pane这样的目标描述也会显示pane_id。比如看到一个0:1.2意思是会话 0、窗口 1、窗格 2。抓这个窗格的内容可以写tmux capture-pane -t 0:1.2 -S -2000 -p /tmp/from-pane12.log在脚本和绑定里我更推荐用#{pane_id}这种变量比如tmux capture-pane -t #{pane_id} -S -2000 -p | xclip -selection clipboard因为pane_id在整个 tmux server 生命周期内是稳定的不像窗格编号那样会随拆分合并而变动。这个习惯养成后脚本稳定性会明显提升。4. 进阶玩法OSC52、封装脚本和常见坑4.1 OSC52复制之后自动同步到本地剪贴板前面提到的 capture-pane 管道方案本质是“手动指定流向”。还有一个更自动的方案很多人都忽略了tmux 的set-clipboard配置。它的作用是在你从 copy mode 按下回车复制时tmux 通过终端发送一段 OSC52 转义序列由终端把内容写入系统剪贴板。也就是说如果你在.tmux.conf里写了set -g set-clipboard on然后正常用 copy mode 选中一小段内容复制动作结束的一瞬间内容就已经在系统剪贴板里了可以直接Ctrl-v粘贴。不需要 xclip也不需要 pbcopy。这个方案听起来最优雅但有明显前提你的终端必须支持 OSC52。比较常见的比如 iTerm2、kitty、较新版本的 Windows Terminal都对 OSC52 支持得不错。如果你的终端不支持这段配置不会生效也不会报错只是系统剪贴板里始终没有内容罢了。我的态度是把set-clipboard on当作“增强项”该配置配置上。日常快速复制小段内容用到它的概率很高真正要抓整窗格的时候还是优先绑定的prefix C/prefix P因为 OSC52 只处理你选择的内容不会自动帮你抓一整个窗格几千行。4.2 封装成脚本参数化才是长期好用的关键快捷键绑多了以后我很快发现一个问题写在.tmux.conf里的命令一旦复杂起来引号转义就非常折磨人。后来我把逻辑抽到了独立脚本里配置文件只留一行调用代码。这样的好处是脚本可以用 bash 完整写条件判断也方便多加几个参数。我现在的~/.tmux/scripts/dump-pane.sh大致长这样#!/usr/bin/env bash # 保存当前窗格内容到文件 # 用法: dump-pane.sh [目标文件] set -euo pipefail TARGET${1:-$HOME/tmux-dumps/pane-$(date %Y%m%d-%H%M%S).log} mkdir -p $(dirname $TARGET) tmux capture-pane -t . -S -3000 -J -p $TARGET echo saved to: $TARGET剪贴板版本是这个样子#!/usr/bin/env bash # 复制当前窗格内容到本地剪贴板 set -euo pipefail if command -v xclip /dev/null 21; then tmux capture-pane -S -3000 -J -p | xclip -selection clipboard elif command -v pbcopy /dev/null 21; then tmux capture-pane -S -3000 -J -p | pbcopy elif command -v clip.exe /dev/null 21; then tmux capture-pane -S -3000 -J -p | clip.exe else echo no clipboard tool found, output to stdout instead 2 tmux capture-pane -S -3000 -J -p fi然后在.tmux.conf里绑定bind D run-shell ~/.tmux/scripts/dump-pane.sh bind L run-shell ~/.tmux/scripts/copy-pane.sh这里有个值得专门说明的细节脚本里我用了一串command -v来判断系统里到底有哪些剪贴板工具。不同发行版、不同工作环境装的东西千差万别。直接写死xclip切到 Wayland 环境就会哑火写死pbcopy回到 Linux 又找不到命令。所以脚本应该“探测环境、选择工具、执行动作”而不是“默认我有某个工具”。这套模式不光适用于 tmux放在任何跨平台 shell 工具里都成立。4.3 常见问题与排查速查表这部分我总结了实际操作里遇到的几个高频问题直接整理成了一张表。现象原因解决办法tmux capture-pane -S -3000 -p拿到的内容还是只有一屏tmux 的history-limit设置太小历史行数不足 3000在.tmux.conf里设置set -g history-limit 10000或更大重开会话后生效保存的文件里全是^[[31m这类颜色控制字符可能误加了-e参数或者源程序输出本身就带 ANSI 控制序列去掉-e如果已经生成用sed s/\x1b\[[0-9;]*m//g清洗复制到剪贴板后内容被截断剪贴板工具对超长文本有处理上限常见于 xclip改走文件输出不要硬塞剪贴板几 MB 以上的内容建议直接落盘文件里的长行被切成一段段贴出来排版全乱了没有加-J终端宽度折行被原样保留统一在 capture-pane 后面加-J从 copy mode 复制后系统剪贴板里没内容set-clipboard未开启或终端不支持 OSC52改用管道方案直接| xclip -selection clipboardrun-shell绑定后提示date: command not foundtmux 子 shell 的PATH不完整在.tmux.conf里补setenv -g PATH $PATH或改用绝对路径抓到的内容不是目标窗格-t写错了目标或者使用了不稳定的窗格编号用#{pane_id}这类稳定标识或先运行tmux list-panes -a确认历史内容抓不全窗格内的 history 在创建窗格时被限制或者会话重启导致历史丢失调大history-limit并把重要输出及时落盘每一次踩坑本质上都是在提醒我tmux 的历史缓冲区是窗格内容的唯一来源历史被截断capture-pane 再好也无力回天。所以我把history-limit调到了 20000。内存会稍微多一些但一个窗格几千行文本在内存里真的不算什么比起抓不到内容带来的损失这点成本很划算。还有一个小坑capture-pane默认抓到的范围是当前可见区容易让人误以为命令无效。其实只要默认不带-S就只输出一屏。这不算 bug是行为预期问题。所以看到“只有一屏”的时候先想想是不是历史行数不足以及是不是没写-S。4.4 几个提升体验的操作心得这套方案用了快一年有几个心得算是“不写下来会忘”级别的。第一文件名一定要带时间戳。保存内容这件事绝大多数场景都是“防丢”而不是“归档到固定文件”。如果每次写同一个路径第二次就把第一次覆盖了。我现在的文件命名格式是pane-YYYYmmdd-HHMMSS.log保证了基本不会撞名。脚本里用date %sUnix 时间戳也可以二者选一个就行。第二剪贴板方案和文件方案不要混着用。有时图省事我尝试过把剪贴板的内容再手动粘进文件结果发现粘贴过程中如果触发终端多行处理格式会莫名其妙多出空行。现在已经固定下来想长期保存就走prefix C落盘想临时粘贴就走prefix P进剪贴板。两个通道各管各的互不污染。第三配置文件本身纳入版本管理。.tmux.conf里的绑定、脚本里的判断都算是我个人的“生产工具”了。我会把整个~/.tmux/目录用 git 管理换电脑或重装系统后直接拉下来软链到 home 目录就能恢复完整环境。绑定的快捷键也一样只要配置在习惯就不会丢。第四“全选输出”不等于“无限输出”。再大的history-limit也有上限而且 tmux 重启、关闭窗格历史记录照样会丢失。真正重要的输出应该让程序自己写日志文件或者在任务结束时顺手按两下prefix C落盘。把 tmux 当成“短期流动缓存”把文件系统当成“长期归档”这个边界想清楚就不会在关键时刻丢东西。5. 写在最后的经验这篇文章里的命令和配置几乎都是我在真实工作流里一遍遍打磨出来的。我现在最常用的动作就两个prefix C保存当前窗格到~/tmux-dumps/prefix P复制到本地剪贴板。偶尔需要精确截取某一段历史时再进 copy mode 配合 vi 快捷键手工选中。自从用了这套流程我再也没有因为“终端复制粘贴出错”这件事烦心过。如果你刚开始使用 tmux建议先复制第三节里最简单的两条命令去试跑通之后再改造成自己的快捷键和脚本。这里所有配置都以 tmux 3.x 为准不同小版本之间命令差异极小放心使用。把这套东西放进自己的~/.tmux.conf里慢慢就会感觉到tmux 不只是窗口管理器它还是你处理文本的“第二双手”。