如果你有过这样的经历——SSH 连着服务器跑一个几小时的编译/迁移任务,网断了,任务也死了;或者每天开发前要手动开五个终端标签页,一个跑服务、一个跑日志、一个连数据库、一个跑测试、一个敲 git——那 tmux 就是为你准备的。
tmux(terminal multiplexer,终端复用器)做的事一句话概括:把"终端窗口"和"跑在里面的进程"解耦。进程活在一个常驻后台的 tmux 服务器里,你的终端只是一个随时可以拔掉、换台机器再插上的显示器。
本文分三部分:先讲原理(理解了原理,后面所有行为都不需要死记)、再讲使用教程(从零到能用的最短路径 + 速查表)、最后讲tmux 到底能做什么(实战场景与脚本)。
本文所有命令、按键、默认值均以 tmux 3.7b 为准(较老的 2.x 版本部分选项名和默认值不同,文中会标注)。
一、原理:tmux 到底是怎么工作的#
1.1 客户端 / 服务器架构#
这是理解 tmux 的第一把钥匙:你输入的 tmux 命令不是"那个跑着你程序的东西",它只是个客户端。
你的终端 (Alacritty / iTerm / Windows Terminal / ssh)
│ 键盘输入 / 屏幕输出
▼
┌─────────────┐ Unix domain socket
│ tmux client │◄──────────────────────────────┐
└─────────────┘ /tmp/tmux-1000/default │
▼
┌────────────────────────────┐
│ tmux server │
│ (常驻后台的单个进程) │
│ │
│ session: work │
│ ├── window 0: editor │
│ │ ├── pane %0 ──► nvim
│ │ └── pane %1 ──► go run
│ └── window 1: logs │
│ └── pane %2 ──► tail -f
│ session: ops │
│ └── window 0 ──► bash │
└────────────────────────────┘几个关键事实:
| 事实 | 含义 |
|---|---|
| server 是单个进程,管理所有 session | tmux kill-server 会一次干掉所有会话,慎用 |
| client 与 server 通过 Unix socket 通信 | 默认路径 /tmp/tmux-$UID/default,可用 -L name 换 socket 名、-S path 换路径 |
| 第一次执行 tmux 时 server 才被拉起 | 之后所有 tmux xxx 命令都只是"给 server 发一条指令" |
| client 不持有任何业务进程 | 所以 client 死掉(网断、终端关闭)对 pane 里的进程毫无影响 |
| server 内部是 libevent 事件循环 | 单线程处理所有 pty 读写、client 通信、定时器(如状态栏刷新) |
你可以亲眼验证这个架构:
# 看进程树:server 和 client 是两个独立进程
pgrep -a tmux
# 3721 tmux: server
# 8899 tmux attach
# socket 就是一个文件
ls -l /tmp/tmux-$(id -u)/
# srwxrwx--- 1 zhxu zhxu 0 Jul 30 08:39 default
# 用不同 socket 起一套完全独立的 tmux(互不可见)
tmux -L test new -s foo1.2 伪终端(pty):断线不死的真正原因#
第二把钥匙是 pty(pseudo terminal,伪终端)。
Linux 上的终端设备是一对 master/slave:写进 master 的数据从 slave 读出来,反之亦然。tmux server 为每个 pane 创建一对 pty,自己拿着 master 端,然后 fork 出 shell,把 shell 的 stdin/stdout/stderr 都接到 slave 端,并把 slave 设为该进程的控制终端。
┌───────────────── tmux server ─────────────────┐
│ │
client │ pty master ◄────► [ 终端仿真 + grid 缓冲 ] │
◄──────┼───────────────┐ │
│ │ │
└───────────────┼───────────────────────────────┘
│
pty slave (/dev/pts/7)
│ 作为控制终端
▼
bash ──► go run ./main.go于是就有了下面这条因果链,它解释了 tmux 最核心的价值:
你的程序的控制终端,是 tmux server 持有的 pty,而不是 ssh 分配的那个 pty。 ssh 断开时,内核只会向 ssh 那个 pty 的前台进程组发
SIGHUP——而你的程序根本不在那里。 tmux server 是 ssh 的子进程吗?不是,它在第一次启动时就 daemon 化脱离了。 所以:网断了,server 活着,pane 里的进程连一个信号都收不到,它甚至不知道发生了什么。
这也顺带解释了另外几个现象:
- 为什么 tmux 里能正常用 vim、htop、
Ctrl-C、作业控制? 因为 pane 里是一个真正的 pty,有控制终端、有前台进程组、支持 termios(行编辑、信号字符)。这是 tmux 和nohup/&的根本区别——后者只是重定向了 stdout,程序拿不到交互终端。 - 为什么
tail -f在 pane 里知道自己"不是管道"?isatty()返回真。很多程序(git、ls、docker)会因此保留彩色输出,这也是tmux capture-pane抓到的内容带颜色的原因。 - 为什么改变窗口大小,vim 会自动重排? tmux server 检测到 client 尺寸变化,
ioctl(TIOCSWINSZ)改 pty 尺寸,内核向前台进程组发SIGWINCH。
1.3 屏幕状态:为什么重连后画面还在#
nohup ./task & 也能让任务在断线后活着,但你再也看不到它之前打印了什么。tmux 能,因为 server 内部为每个 pane 维护了一份屏幕状态(grid):
pane 的内存结构(简化)
├── 可见屏幕:80×24 的字符网格,每格存 字符 + 前景色 + 背景色 + 属性(粗体/下划线…)
├── scrollback 历史:向上滚动的行,上限由 history-limit 决定(默认仅 2000 行)
└── 终端状态机:光标位置、当前 SGR 属性、备用屏幕(alternate screen)、模式标志…tmux server 本身就是一个终端仿真器:它解析程序输出的 ANSI 转义序列(\e[31m 变红、\e[2J 清屏、\e[?1049h 切备用屏幕),把结果画进 grid。你 attach 的那一刻,server 只是把 grid 重新渲染成转义序列发给你的真实终端——所以画面"还在"。
由此推出两个非常实用的结论:
history-limit默认只有 2000 行,跑长任务时上翻找不到早期日志是必然的,配置里应该调大(见 4.1)。- 因为 grid 在 server 内存里,可以随时程序化读取——这就是
tmux capture-pane -p能把任意 pane 的当前内容打印到 stdout 的原理,自动化脚本里非常好用(见 5.4)。
1.4 三层对象模型:session / window / pane#
session(会话) ── 一个工作上下文,attach/detach 的单位,独立于任何终端
└── window(窗口) ── 相当于"标签页",占满整个可见区域,同一时刻只显示一个
└── pane(窗格) ── 窗口内的矩形分割,每个 pane 一个 pty + 一个进程| 对象 | 编号/标识 | 常见误解 |
|---|---|---|
| session | 名字(work)或序号 | 不是"一个终端窗口",而是一组窗口的集合 |
| window | session:index(work:1) | 默认从 0 开始(base-index 0),常改成 1 更顺手 |
| pane | %0、%1(全局唯一 ID) | pane ID 不随移动改变,写脚本时用 %id 比用序号可靠 |
值得单独说的是 window 可以被多个 session 共享(link-window),session 可以成组(new-session -t existing:新 session 与原 session 共享同一批 window,但各自独立地"当前显示哪个 window")。这是"两个人看同一批窗口、但各看各的"的底层机制。
多个 client 同时 attach 同一个 session 时,尺寸冲突由 window-size 选项决定(默认 latest,以最近活跃的客户端为准;也可设 smallest/largest/manual)。
1.5 前缀键为什么必须存在#
pane 里跑着 vim、emacs、htop,它们要吃掉几乎所有按键。tmux 必须能区分"这个键是给我的"还是"透传给程序",所以引入了前缀键(prefix):默认 Ctrl-b,按下后 tmux 进入"下一个键归我"的状态。
本质上 tmux 维护若干键表(key table):
| key table | 何时生效 |
|---|---|
root | 平时(默认几乎为空,所以按键都透传给程序) |
prefix | 按下前缀键之后的那一个键 |
copy-mode / copy-mode-vi | 进入复制模式后 |
tmux list-keys -T prefix # 看前缀键表里所有绑定
tmux list-keys -T root # 看直接生效的绑定(鼠标绑定都在这里)理解这一点,你就知道为什么嵌套 tmux(本地 tmux 里 ssh 到远端再开 tmux)需要按两次前缀:外层 tmux 先吃掉 C-b,再按 C-b(绑定为 send-prefix)才会把一个真正的 C-b 字节透传给内层。
1.6 双层终端仿真与 TERM#
tmux 的位置很特殊:它对上是终端应用(向你的真终端输出转义序列),对下是终端仿真器(为 pane 里的程序提供终端能力)。
真终端 (支持真彩色、OSC 52、图片协议…)
▲ tmux 按 外层 TERM 的能力输出
│
tmux server ── 内部维护 grid
│ 向 pane 内程序宣告能力:TERM=tmux-256color
▼
nvim / htop / less这就是 tmux 里颜色/按键/剪贴板"偶尔出问题"的根源——能力必须在两层都存在:
- 内层程序通过
TERM(tmux 3.x 默认给tmux-256color;2.x 老版本默认是screen,只有 8 色)查 terminfo 决定敢用什么。 - tmux 自己是否把某些能力转发给外层真终端,取决于
terminal-features/terminal-overrides(真彩色RGB、Ctrl-Shift类扩展按键extended-keys、剪贴板set-clipboard走 OSC 52 等)。
所以正确的姿势是:外层终端的 TERM 要如实(如 xterm-256color),tmux 内的 default-terminal 用 tmux-256color,真彩色靠 terminal-features 补声明——而不是到处乱写 TERM=xterm-256color。具体配置见 4.1。
1.7 一切都是命令#
最后一把钥匙:tmux 没有"按键功能"这种东西,只有命令。 按键只是命令的绑定,配置文件只是一串开机执行的命令,tmux 命令行也是把命令发给 server:
# 这三者执行的是同一个东西
# ① 按键:C-b %
# ② tmux 命令提示符(C-b :)里输入:split-window -h
# ③ shell 里:
tmux split-window -h所以:凡是你能手动做的操作,都能被脚本调用。这是第五章所有自动化玩法的基础。
二、安装与最短上手路径#
2.1 安装#
# Debian / Ubuntu
sudo apt install tmux
# Arch
sudo pacman -S tmux
# macOS
brew install tmux
# CentOS / RHEL
sudo yum install tmux
tmux -V # 确认版本,3.x 以上默认值更合理2.2 五分钟能用起来#
tmux # 开一个匿名会话(不推荐,名字是 0、1、2…)
tmux new -s work # ✅ 开一个叫 work 的会话
# ……干活……
# 按 C-b d 脱离(detach),任务继续跑
tmux ls # 看有哪些会话
# work: 2 windows (created Thu Jul 30 09:00:20 2026)
tmux attach -t work # 重新接回来(可简写 tmux a -t work)
tmux kill-session -t work # 确认不要了再关最该先背下来的一条命令是这个,它解决了"到底该 new 还是 attach"的纠结:
tmux new -A -s work # 存在就 attach,不存在就创建(-A = attach if exists)放进 ~/.zshrc / ~/.bashrc 里当别名,日常只需要敲 w:
alias w='tmux new -A -s work'2.3 最该先记住的六个按键#
新手不要一次背全表,先记这六个,别的现查:
| 按键 | 作用 |
|---|---|
C-b d | detach,把会话丢到后台(最重要) |
C-b c | 新建 window |
C-b n / C-b p / C-b 数字 | 切 window |
C-b % / C-b " | 左右分屏 / 上下分屏 |
C-b 方向键 | 在 pane 之间跳 |
C-b z | 当前 pane 全屏放大 / 还原(zoom,救命键) |
记不住时:C-b ? 列出所有绑定(q 退出),C-b / 后按某个键可以查那个键干什么。
三、按键与命令速查表#
以下均为 tmux 3.7b 默认绑定(前缀 C-b)。左列是按键,右列括号里是等价命令,方便写脚本时对照。
3.1 会话(session)#
| 按键 | 作用(等价命令) |
|---|---|
C-b d | detach(detach-client) |
C-b $ | 重命名当前会话(rename-session) |
C-b s | 交互式选择会话/窗口树(choose-tree -Zs) |
C-b ( / C-b ) | 切到上一个 / 下一个会话(switch-client -p/-n) |
C-b L | 切回上一次的会话(switch-client -l) |
C-b D | 选择一个客户端把它踢下线(choose-client) |
C-b C-z | 挂起客户端(suspend-client,回 shell 后 fg 回来) |
命令行侧:
tmux ls # list-sessions
tmux new -s name -d # 后台创建,不 attach
tmux new -A -s name # 有则 attach,无则创建
tmux attach -t name # attach
tmux attach -d -t name # attach 并把其他客户端踢掉(-d = detach others)
tmux attach -r -t name # 只读 attach(read-only,演示/围观用)
tmux rename-session -t old new
tmux kill-session -t name
tmux has-session -t name 2>/dev/null && echo exists # 脚本里做判断3.2 窗口(window)#
| 按键 | 作用(等价命令) |
|---|---|
C-b c | 新建窗口(new-window) |
C-b , | 重命名窗口(rename-window) |
C-b & | 关闭窗口(kill-window,会二次确认) |
C-b n / C-b p | 下一个 / 上一个窗口 |
C-b 0…C-b 9 | 按序号跳转 |
C-b l | 切回上一个窗口(last-window) |
C-b w | 交互式窗口列表(choose-tree -Zw) |
C-b f | 按内容搜索窗口(find-window,忘了在哪个窗口时神器) |
C-b . | 修改当前窗口序号(move-window) |
C-b ' | 输入序号跳转 |
3.3 窗格(pane)#
| 按键 | 作用(等价命令) |
|---|---|
C-b % | 左右分屏(split-window -h) |
C-b " | 上下分屏(split-window -v) |
C-b 方向键 | 切换 pane(select-pane -L/-R/-U/-D) |
C-b o / C-b ; | 顺序切下一个 / 切回上一个 pane |
C-b q | 显示 pane 编号(紧接着按数字直接跳) |
C-b z | zoom:当前 pane 临时全屏(resize-pane -Z) |
C-b x | 关闭 pane(kill-pane,会二次确认) |
C-b Space | 循环切换预设布局(next-layout) |
C-b M-1…M-7 | 直接套用布局:均分横排 / 均分竖排 / 主区上下 / 主区左右 / 平铺 … |
C-b { / C-b } | 与上一个 / 下一个 pane 互换位置(swap-pane) |
C-b C-o | 整体轮转所有 pane(rotate-window) |
C-b C-方向键 | 调整 pane 大小(1 格,可长按连发) |
C-b M-方向键 | 调整 pane 大小(5 格) |
C-b ! | 把当前 pane 拆成独立窗口(break-pane) |
C-b * | 新建浮动窗格(new-pane,较新版本特性) |
C-b t | 满屏时钟(clock-mode,纯好玩) |
跨窗口/会话搬 pane(很多人不知道 tmux 能这么干):
# 把 3 号窗口的 pane 拉到当前窗口,左右并排
tmux join-pane -h -s 3
# 把当前 pane 送到 logs 窗口
tmux join-pane -t logs
# 把 pane 独立成窗口
tmux break-pane或者交互式:先在源 pane 按 C-b m 打标记(mark),再到目标窗口执行 join-pane。
3.4 复制模式与剪贴板#
C-b [ 进入复制模式(也可以上翻页 C-b PageUp 直接进)。默认是 emacs 键位(mode-keys emacs),习惯 vi 的记得在配置里改成 vi。
vi 键位下(推荐配置见 4.1):
| 按键 | 作用 |
|---|---|
C-b [ | 进入复制模式 |
h j k l / w b / g G | 移动(同 vim) |
C-u / C-d | 上/下翻半页 |
/ / ? 然后 n N | 向下/向上搜索、跳下一个/上一个匹配 |
Space | 开始选择 |
v | 开始选择(vi 风格,需配置绑定) |
C-v | 切换矩形(块)选择 |
y / Enter | 复制并退出 |
q / Esc | 退出复制模式 |
C-b ] | 粘贴最近的缓冲区(paste-buffer) |
C-b = | 从粘贴板列表里选一个粘(choose-buffer) |
C-b # | 列出所有粘贴板(list-buffers) |
注意 tmux 的 buffer 和系统剪贴板是两回事。 打通两者有两条路:
# 路线 A:OSC 52(推荐,跨 SSH 也能用,前提是外层终端支持)
set -s set-clipboard on
# 路线 B:管到本机剪贴板程序(copy-pipe)
# Linux/X11:
bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel 'xclip -selection clipboard -i'
# Wayland:
bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel 'wl-copy'
# macOS:
bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel 'pbcopy'命令行侧同样可用,脚本里很方便:
tmux show-buffer # 打印当前 buffer
tmux save-buffer out.txt # 存文件
tmux load-buffer <(pbpaste) # 从外部灌进 tmux buffer
tmux set-buffer "hello" # 直接塞内容3.5 其他常用#
| 按键 | 作用 |
|---|---|
C-b : | 命令提示符,可执行任意 tmux 命令(万能出口) |
C-b ? | 列出所有键绑定 |
C-b i | 显示当前窗口信息(display-message) |
C-b ~ | 查看 server 消息日志(排查配置报错) |
C-b C | 交互式选项编辑器(customize-mode,忘了选项名时很好用) |
C-b r | 刷新客户端(画面花了时按,refresh-client) |
C-b > / C-b < | 弹出 pane / window 操作菜单(鼠标党友好) |
四、配置:一份可以直接用的 tmux.conf#
4.1 推荐配置(带解释)#
tmux 启动时按顺序加载 /etc/tmux.conf,然后是 ~/.tmux.conf 或 $XDG_CONFIG_HOME/tmux/tmux.conf(3.1+ 支持后者,喜欢干净 home 目录的用它)。
# ============ 基础手感 ============
# 前缀键改成 C-a(更好按;代价是和 shell 的行首快捷键冲突,
# 所以再绑一个 C-a C-a 把真正的 C-a 送进去)
set -g prefix C-a
unbind C-b
bind C-a send-prefix
# 窗口/窗格从 1 开始编号,关掉窗口后自动重排序号
set -g base-index 1
setw -g pane-base-index 1
set -g renumber-windows on
# scrollback 默认只有 2000 行,太小
set -g history-limit 50000
# 开鼠标:滚轮翻历史、点击切 pane、拖边框调大小
set -g mouse on
# vi 风格的复制模式(默认是 emacs)
setw -g mode-keys vi
bind -T copy-mode-vi v send-keys -X begin-selection
bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel 'wl-copy' # 按系统替换
set -s set-clipboard on # 走 OSC 52,跨 SSH 也能复制到本机
# ESC 键延迟:3.x 默认已是 10ms,老版本是 500ms(会让 vim 的 ESC 发木)
set -s escape-time 10
# 让 vim 的 FocusGained/autoread 生效
set -g focus-events on
# ============ 颜色与终端能力 ============
set -g default-terminal "tmux-256color"
# 向外层真终端声明真彩色能力(把 xterm-256color 换成你外层实际的 TERM)
set -as terminal-features ",xterm-256color:RGB"
# ============ 更顺手的绑定 ============
# 分屏用 | 和 -(形状即所见),并继承当前目录
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
unbind '"'
unbind %
# 新窗口也继承当前目录
bind c new-window -c "#{pane_current_path}"
# 无前缀切 pane(Alt+方向键)
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D
# 一键重载配置
bind R source-file ~/.tmux.conf \; display-message "tmux.conf reloaded"
# 同步输入到所有 pane(批量操作多台机器时开/关)
bind S setw synchronize-panes \; display-message "sync #{?pane_synchronized,ON,OFF}"
# 弹出层里开一个临时 shell(scratch),再按一次关掉
bind g display-popup -E -w 80% -h 80% -d "#{pane_current_path}"
# ============ 状态栏 ============
set -g status-position top
set -g status-interval 5
set -g status-left " #[bold]#S #[default]"
set -g status-right " #{?pane_synchronized,#[reverse] SYNC #[default] ,}%Y-%m-%d %H:%M "
setw -g automatic-rename on改完不用重启 server:C-b : 输入 source-file ~/.tmux.conf,或者用上面绑的 C-b R。
⚠️ 配置里写错选项名不会有弹窗,只会静默失败——
C-b ~(show-messages)能看到报错。
4.2 关于插件(TPM)#
tmux-plugins/tpm 是社区标准插件管理器。真正值得装的其实就两三个:
| 插件 | 解决什么 |
|---|---|
tmux-resurrect | 把会话布局/窗口/工作目录存盘,服务器重启后能恢复(tmux server 一死,session 就没了,这是 tmux 唯一"不持久"的地方) |
tmux-continuum | 给 resurrect 加自动定时保存 |
vim-tmux-navigator | 用同一套 C-h/j/k/l 在 vim 分屏和 tmux pane 之间无缝跳 |
其余美化类插件按需,不装也完全够用。如果你不想引入插件,tmux-resurrect 的替代方案是自己写一个"布局启动脚本"(见 5.3)——很多人最后都走到了这条路上,因为可复现的布局脚本比恢复快照更可靠。
五、tmux 能做什么:实战场景#
5.1 长任务不怕断线(最基础也最值钱)#
ssh server
tmux new -A -s migrate
./long_migration.sh # 跑起来
# C-b d 脱离,直接关笔记本回家
# 第二天,换台机器
ssh server
tmux a -t migrate # 画面、滚动历史、进度条全都在比 nohup 强的地方:有交互终端(可以中途 Ctrl-C、可以回答 y/n 提示、可以在旁边新开 pane 看日志),且能看到历史输出。
5.2 一屏搞定开发环境#
典型布局:左边编辑器占大半,右上跑服务,右下跑测试/日志。
┌──────────────────────┬──────────────┐
│ │ go run . │
│ nvim ├──────────────┤
│ │ tail -f log │
└──────────────────────┴──────────────┘手动搭:C-b % 分左右 → 右边 C-b " 分上下 → C-b M-方向键 调宽度。搭好后 C-b z 可以随时把任意 pane 临时放大看细节。
5.3 用脚本一键拉起整套工作区#
因为"一切都是命令",工作区可以完全脚本化。把下面存成 ~/bin/dev:
#!/usr/bin/env bash
set -euo pipefail
SESSION="dev"
ROOT="$HOME/GolandProjects/lee"
# 已存在就直接接进去,不重复创建
if tmux has-session -t "$SESSION" 2>/dev/null; then
exec tmux attach -t "$SESSION"
fi
# 后台创建会话和第一个窗口
tmux new-session -d -s "$SESSION" -c "$ROOT" -n editor
tmux send-keys -t "$SESSION:editor" 'nvim .' C-m
# 第二个窗口:上下两个 pane,一个跑服务一个跑日志
tmux new-window -t "$SESSION" -c "$ROOT" -n server
tmux send-keys -t "$SESSION:server" 'hugo server -D' C-m
tmux split-window -t "$SESSION:server" -v -c "$ROOT"
tmux send-keys -t "$SESSION:server.2" 'git status' C-m
# 第三个窗口:留个干净 shell
tmux new-window -t "$SESSION" -c "$ROOT" -n shell
tmux select-window -t "$SESSION:editor"
exec tmux attach -t "$SESSION"一行流版本(临时用,不值得存文件时):
tmux new -d -s tmp 'top' \; split-window -d 'htop' \; attach想要声明式配置的话,
tmuxinator(Ruby)/tmuxp(Python)用 YAML 描述布局,本质上就是帮你生成上面这堆命令。项目不多的话,一个 shell 脚本反而更好维护。
5.4 把 tmux 当成"自动化机器人的手"#
这是很多人忽略的用法:tmux 让脚本能操作一个交互式终端会话——包括那些没有 API、只能人肉敲的程序。
# 向某个 pane 敲键盘(C-m = 回车)
tmux send-keys -t dev:server.1 'make build' C-m
# 抓取 pane 当前屏幕内容(-p 打到 stdout,-S -100 含最近 100 行历史)
tmux capture-pane -p -t dev:server.1 -S -100 | tail -20
# 轮询等某个关键字出现(简易"等服务起好")
until tmux capture-pane -p -t dev:server.1 | grep -q 'Listening on'; do sleep 1; done
# 把某个 pane 的输出实时落盘(开关式)
tmux pipe-pane -t dev:server.1 -o 'cat >> /tmp/server.log'
# 读取结构化信息,写监控脚本很好用
tmux list-panes -a -F '#{session_name}:#{window_index}.#{pane_index} #{pane_current_command} #{pane_pid}'值得一提的现实场景:跑长时间的 AI 编码 agent / CLI 工具。这类进程既要交互(要能看输出、随时插话),又动辄跑几十分钟,还怕网断——正好是 tmux 的靶心。给每个项目开一个命名会话(cc-blog、cc-homelab),断线后 tmux a -t cc-blog 接回去,全程不丢上下文;配合 capture-pane 还能在外部脚本里检查它进展到哪了。
5.5 同时操作多台机器#
# 四个 pane 分别 ssh 到四台机器
tmux new -s fleet \; \
send-keys 'ssh web1' C-m \; \
split-window -h 'ssh web2' \; \
split-window -v 'ssh web3' \; \
select-pane -t 1 \; split-window -v 'ssh web4' \; \
select-layout tiled
# 打开输入同步:敲一次,所有 pane 一起执行
# C-b : setw synchronize-panes on (或用 4.1 里绑的 C-b S)同步输入是把双刃剑,
rm -rf也会同步。养成用完立刻关掉的习惯,状态栏上加个 SYNC 提示(见 4.1 配置)。
5.6 结对编程 / 教学演示 / 远程支援#
同一台机器上的两个用户 attach 同一个 session,就能看到同一个屏幕。
# 方式一:同一用户、多个终端 attach 同一会话(最简单)
tmux a -t work # 两边都看到同样画面,鼠标/键盘都能操作
tmux a -r -t work # 围观者用只读模式,防手滑
# 方式二:两人各看各的窗口,但共享同一批窗口(会话组)
tmux new-session -t work -s work-guest
# 方式三:不同用户共享(需要放开 socket 权限,注意安全边界)
tmux -S /tmp/pair new -s pair
chmod 1777 /tmp/pair # 另一用户: tmux -S /tmp/pair attach
tmux server-access -w otheruser # 3.3+ 显式授权写权限跨公网协作现在更常用 tmate(tmux 的 fork,自带中转服务器,一条链接分享);如果双方本来就能 ssh 到同一台机器,原生 tmux 就够了。
5.7 日志/监控墙#
tmux new -s watch -d 'journalctl -f -u caddy'
tmux split-window -t watch -v 'journalctl -f -u artalk'
tmux split-window -t watch -h 'htop'
tmux select-layout -t watch tiled
tmux a -t watch配合 C-b z 放大某一格看细节,比开四个终端标签页来回切要顺手得多。
5.8 弹出层当"随手终端"#
# 临时窗口,退出即销毁(-E),不打乱现有布局
tmux display-popup -E -w 80% -h 80% -d "#{pane_current_path}"
# 弹出层里跑单个命令
tmux display-popup -E -w 70% -h 70% 'lazygit'绑到一个键上(4.1 里的 C-b g),就得到一个"随时呼出、随时收起"的临时 shell —— 查个文档、跑个 git、看眼 k9s,不用为此新建 window。
5.9 tmux 不该做什么#
诚实地说清楚边界,能省掉很多坑:
- ❌ 不要用 tmux 当服务守护进程。
tmux new -d 'my-server'能跑,但没有自动重启、没有日志轮转、开机不自启、kill-server就全没。生产上用 systemd(systemd-run/user service),tmux 留给"人要看的东西"。 - ❌ 不要指望 tmux 持久化到重启之后。server 进程一死,所有 session 都消失(
tmux-resurrect或布局脚本才是答案)。 - ❌ 不要用它替代终端本身的能力。图片显示、字体、GPU 渲染归终端管;tmux 的双层仿真反而可能挡住这些特性(如部分图片协议、部分扩展按键)。
六、常见坑与排查#
| 现象 | 原因 | 解法 |
|---|---|---|
| tmux 里颜色变少 / vim 主题发灰 | TERM 只宣告了 8/16 色(老版本默认 screen) | set -g default-terminal "tmux-256color",真彩色再加 set -as terminal-features ",xterm-256color:RGB" |
| 外面能显示的颜色,进 tmux 就丢 | 外层终端 TERM 被写错,或没声明 RGB 能力 | 检查 tmux 外 echo $TERM,别到处硬写 xterm-256color |
vim 里按 ESC 反应发木 | 老版本 escape-time 默认 500ms | set -s escape-time 10(3.x 已默认 10) |
| 上翻找不到几分钟前的日志 | history-limit 默认仅 2000 行 | set -g history-limit 50000 |
| 鼠标选中复制不到系统剪贴板 | tmux buffer ≠ 系统剪贴板 | set -s set-clipboard on,或用 copy-pipe 接 wl-copy/pbcopy/xclip |
| 开了 mouse 后没法用鼠标选文字了 | 鼠标事件被 tmux 接管 | 按住 Shift 拖拽走终端原生选择;或复制模式里选 |
| 嵌套 tmux 时前缀键失效 | 外层先吃掉了前缀 | 内层操作按两次前缀(send-prefix),或给内外层设不同前缀 |
Ctrl-方向键、Ctrl-Shift-x 在 tmux 里不生效 | 扩展按键默认不转发 | set -s extended-keys on(并确认外层终端支持) |
vim 的 autoread / 焦点事件失灵 | focus-events 默认 off | set -g focus-events on |
| 修改了配置没反应 | 配置只在 server 启动时执行;或选项名写错静默失败 | source-file 重载;C-b ~ 看错误消息 |
tmux ls 说 no server running | server 已退出(所有 session 都关了/被 kill) | 正常现象,tmux new -s x 重开即可 |
| 误关了所有会话 | 手滑 kill-server | 无法恢复,这就是要装 tmux-resurrect 或写布局脚本的理由 |
七、和其他方案的对比#
| 方案 | 定位 | 相对 tmux |
|---|---|---|
| GNU screen | 老前辈,同样是终端复用器 | 几乎所有系统预装;但分屏能力弱、配置和脚本能力差、社区活跃度低 |
| zellij | Rust 写的现代复用器 | 开箱即用、有界面提示、内置布局文件和插件(WASM);生态与远端普及度不如 tmux |
| dtach / abduco | 只做 detach/attach,不做复用 | 极轻量,没有分屏/状态栏/复制模式;只想"断线不死"时够用 |
| tmate | tmux 的 fork | 专门解决跨公网共享终端,一条链接分享 |
| 终端自带标签页/分屏 | 终端软件的功能 | 本地体验好,但在远端不存在——ssh 一断全没,这是 tmux 不可替代的核心 |
一句话选型:要在服务器上活着、要脚本化、要普适(哪台机器上都有)→ tmux。
八、最小速查卡#
会话 tmux new -A -s name 开/接会话 C-b d 脱离
tmux ls 列会话 C-b s 选会话
tmux a -t name 接会话 C-b $ 改名
窗口 C-b c 新建 C-b , 改名 C-b n/p 切换 C-b 1..9 跳转
C-b w 窗口树 C-b f 搜索 C-b & 关闭
窗格 C-b % 左右分 C-b " 上下分 C-b 方向键 切换 C-b z 放大
C-b x 关闭 C-b Space 换布局 C-b C-方向键 调大小
C-b ! 独立成窗口 C-b q 显示编号
复制 C-b [ 进入 / 搜索 Space/v 选 y 复制 C-b ] 粘贴
万能 C-b : 命令行 C-b ? 所有绑定 C-b ~ 错误日志结语#
tmux 的学习曲线几乎全部集中在"前缀键 + 三层模型"这一小段上,翻过去之后,它就变成了一个几乎无感的基础设施:你的工作上下文不再绑在某个终端窗口、某条网络连接、某台笔记本上。
真正建议投入时间的三件事,收益远大于背快捷键:
- 写一份自己的
tmux.conf(照抄第四章,按手感改); - 给每个常做的项目写一个布局启动脚本(第 5.3 节);
- 记住
capture-pane/send-keys——它们能把 tmux 从"人用的工具"变成"脚本能用的工具"(第 5.4 节)。



