Pairwise
← 全部文章
tmux终端效率工具

tmux 从会用到用好

按初级、熟练、高级三步走:会话 / 窗口 / pane 的基本操作,复制模式与布局整理,再到关机恢复现场和一条命令开好项目。文中有一个能直接在页面上按键的模拟器。

发布 阅读约 8 分钟

我每天的终端工作都开在 tmux 里:一个项目一个会话,编辑器、服务、日志各占一块,关掉终端软件也不会丢。这篇把我用到的东西按三个阶段整理出来:

  • 初级:能用起来。开会话、分窗口、切 pane、离开再回来。
  • 熟练:用得顺手。复制模式翻历史和搜索、布局整理、调整顺序。
  • 高级:让它替你干活。关机后恢复现场、同步输入、一条命令开好项目布局。

命令以 tmux 3.6 的 man page 为准。按键以我的 ~/.tmux.conf 为准,完整配置放在文末。

初级:能用起来

三层结构和基本操作 · 阅读约 15 分钟

学完这一部分,你能开会话、分窗口、切 pane、放大缩小,离开终端后再回来接着干。

三层结构

tmux 把终端分成三层:会话(session)里有多个窗口(window),窗口里切分出多个 pane。

层级 相当于 典型用法
会话 一个工作现场 一个项目一个会话,例如 blog、api
窗口 浏览器标签页 编辑器一个窗口,日志一个窗口
pane 标签页里的分屏 左边写代码,右边跑测试

这三层都由后台的 tmux server 进程持有。关掉终端软件,server 和里面的程序照常运行,这就是 tmux 最大的价值。

会话:开、离开、回来、关

在普通终端里

Terminal window
tmux new -s work # 新建名为 work 的会话并进入
tmux new -A -s work # 已存在就进入,不存在就新建(日常最常用)
tmux ls # 列出所有会话
tmux attach -t work # 回到 work 会话
tmux kill-session -t work # 关掉 work 会话

在 tmux 里面

按键 作用
前缀 d 离开(detach)当前会话,程序继续在后台运行
前缀 s 打开会话列表,上下键选择,回车切换
前缀 $ 重命名当前会话
前缀 : 输入 new -s 名字 在 tmux 里新建会话

在会话列表里删除会话:选中后按 x,再按 y 确认。

窗口:新建、切换、改名、关闭

按键 作用
前缀 c 新建窗口(我的配置会沿用当前 pane 的目录)
前缀 , 重命名当前窗口
前缀 n / 前缀 p 下一个 / 上一个窗口
前缀 1 ~ 前缀 9 直接跳到对应编号的窗口
前缀 w 打开所有会话和窗口的树形列表
前缀 & 关闭当前窗口,按 y 确认

在窗口里最后一个 pane 中执行 exit,窗口也会关闭。

pane:分屏、移动、放大、调大小

按键 作用 来源
前缀 \(或 前缀 %) 左右分屏 我的配置
前缀 -(或 前缀 “) 上下分屏 我的配置
前缀 h/j/k/l 移到左 / 下 / 上 / 右的 pane 我的配置
前缀 H/J/K/L 向左 / 下 / 上 / 右调大小 5 格,可连按 我的配置
前缀 z 放大当前 pane 到整个窗口,再按一次还原 默认
前缀 x 关闭当前 pane,按 y 确认 默认
前缀 q 显示 pane 编号,趁显示时按数字跳过去 默认

分屏都会沿用当前 pane 的目录。我开了 mouse on,也可以用鼠标点选 pane、拖动边框调整大小。

在页面上练一遍

下面是一个按我的配置做的 tmux 模拟器。点一下让它获得焦点,然后按 Ctrl-a 松开,再按操作键;默认的 Ctrl-b 也能用。没有键盘就点下面的按钮。不按前缀时直接打字,会输入到当前 pane 里。

tmux — demo前缀 Ctrl-a

先按 Ctrl-a 进入前缀状态,再按反斜杠左右分屏、减号上下分屏、h j k l 移动、z 放大、c 新建窗口、d 离开、问号查看全部按键。按 Tab 离开模拟器。

tmux按下前缀键后,这里显示等价的 tmux 命令
跟着做 0 / 6
  1. 左右分屏(未完成)
  2. 再上下分屏(未完成)
  3. 在 pane 之间移动(未完成)
  4. 放大一个 pane,再还原(未完成)
  5. 新建窗口,再切回 1 号窗口(未完成)
  6. 离开会话,再回来(未完成)

忘了怎么办

按键 作用
前缀 ? 列出所有快捷键,包括你自己绑定的;按 q 退出
前缀 : 输入任意 tmux 命令,支持 Tab 补全
前缀 r 重新加载 ~/.tmux.conf(我的配置)

熟练:用得顺手

少按键、少用鼠标 · 阅读约 20 分钟 · 前置:初级

初级解决「能用」,这一部分解决「顺手」。最该先练的是复制模式:翻历史、搜索、复制全靠它。其余几节按需取用。

复制模式:翻历史、搜索、复制

我的配置用的是 vi 键位(mode-keys vi),复制会通过 pbcopy 进入系统剪贴板,历史保留 10 万行。

按键 作用
前缀 [ 进入复制模式
前缀 PageUp 进入复制模式并向上翻一页
k / j,Ctrl-u / Ctrl-d 上下移动,翻半页
?关键字 / /关键字 向上 / 向下搜索
n / N 跳到下一个 / 上一个搜索结果
v 选择,y 复制 复制到系统剪贴板并退出,之后用 Cmd-v 粘贴
q 退出复制模式

创建时直接命名

先建再改名要多走一步。创建时带上名字即可:

Terminal window
tmux new -s work -n editor -c ~/workspace # 会话 work,第一个窗口叫 editor,起始目录 ~/workspace
tmux new -d -s logs # 只在后台创建,不进入

在 tmux 里新建命名窗口:前缀 : 输入 new-window -n logs。

也可以绑定快捷键,按下后弹出输入框填名字:

~/.tmux.conf
# 前缀 S:输入名字,新建会话
bind S command-prompt -p "session name:" "new-session -s '%%'"
# 前缀 C:输入名字,在当前目录新建窗口
bind C command-prompt -p "window name:" "new-window -n '%%' -c '#{pane_current_path}'"

快速跳转

按键 作用
前缀 ; 回到上一个 pane,适合在两个 pane 之间来回切换
前缀 ’ 输入编号跳到窗口,适合窗口超过 9 个时
前缀 f 按文字搜索所有窗口
前缀 s / 前缀 w 树形列表:会话、窗口、pane 一起选,右侧带预览

布局一键整理

分屏分乱了,不用手动拖动边框:

按键 作用
前缀 空格 在预设布局之间循环切换
前缀 Alt-1 ~ Alt-7 直接选择某个预设布局

预设布局包括 even-horizontal(左右均分)、even-vertical(上下均分)、main-horizontal / main-vertical(一个大 pane 加一排小 pane)和 tiled(平铺)。也可以用命令指定,例如 select-layout tiled。

调整顺序

会话:靠排序和命名

会话没有固定位置。在 前缀 s 列表里,按 O 切换排序字段,按 r 反转排序。想固定顺序,就在名字前加编号,例如 1-blog。

窗口:交换位置

操作 作用
在 前缀 w 列表里按 Shift-↑ / Shift-↓ 和上一个 / 下一个窗口交换位置
swap-window -d -t -1 / +1 当前窗口左移 / 右移一位,焦点跟着走
swap-window -t 1 和 1 号窗口交换
前缀 . 输入编号,把当前窗口移过去(目标编号需要空着)

pane:交换、拆出、并入

操作 作用
前缀 { / 前缀 } 和上一个 / 下一个 pane 交换位置
前缀 Ctrl-o / 前缀 Alt-o 所有 pane 向前 / 向后轮换
前缀 ! 把当前 pane 拆成独立窗口
join-pane -t :2 把当前 pane 并入 2 号窗口

给 pane 起名字

pane 默认只有编号。可以给它设置标题,再把标题显示在边框上:

Terminal window
# 前缀 : 后输入,给当前 pane 起标题
select-pane -T "api server"
# 写进 ~/.tmux.conf:在每个 pane 顶部显示「编号: 标题」
set -g pane-border-status top
set -g pane-border-format " #{pane_index}: #{pane_title} "

高级:让它替你干活

恢复、同步、脚本化 · 阅读约 20 分钟 · 前置:熟练

这一部分的东西用得不频繁,但每一项都能省下大量重复操作:关机不丢现场、一次输入发到多台机器、一条命令开好整个项目。

关机后恢复现场

我装了 tmux-resurrect(负责保存和恢复)和 tmux-continuum(负责自动保存和开机恢复),并开了 @continuum-restore "on"。在我的机器上实测:存档在 ~/.local/share/tmux/resurrect/,每 15 分钟自动生成一份。

按键 作用
前缀 Ctrl-s 立即保存(关机前按一下,避免丢掉最近 15 分钟的变化)
前缀 Ctrl-r 手动从最新存档恢复

能恢复什么

内容 默认
会话、窗口、pane 的布局、编号、名字 恢复
每个 pane 所在的目录 恢复
vim、less、tail、htop、man 等程序 重新启动
pane 里显示过的文字 不恢复,需开启选项
npm run dev、ssh、claude 等程序 不恢复,只留下空 shell

让恢复更完整

加到 ~/.tmux.conf 里 run "~/.tmux/plugins/tpm/tpm" 那一行之前:

~/.tmux.conf
# 连 pane 里显示的文字一起保存
set -g @resurrect-capture-pane-contents "on"
# 额外重启这些程序,"~" 表示按名字模糊匹配
set -g @resurrect-processes "ssh '~npm run dev'"
# 自动保存间隔改成 5 分钟
set -g @continuum-save-interval "5"

同步输入到所有 pane

打开后,在一个 pane 里输入的内容会同时发送到同一窗口的所有 pane。适合同时登录几台服务器执行同样的命令。

Terminal window
# 前缀 : 后输入
setw synchronize-panes on
# 用完立刻关掉
setw synchronize-panes off

弹出窗口

display-popup 在当前画面上浮出一个临时窗口,关掉程序后自动消失,不会打乱已有布局。

Terminal window
# 前缀 : 后输入:弹出 80% 大小的 htop,退出 htop 后弹窗自动关闭
display-popup -E -w 80% -h 80% htop
# 写进配置:前缀 g 在当前目录弹出 lazygit
bind g display-popup -E -w 90% -h 90% -d "#{pane_current_path}" lazygit

-E 表示程序退出后关闭弹窗,-w / -h 设置宽高,-d 指定起始目录。

一条命令开好项目布局

每天打开同一个项目都要建窗口、分屏、启动服务,可以写成脚本。下面的脚本我在隔离的 tmux 实例里跑通过:会生成 code(2 个 pane)和 logs 两个窗口。

dev.sh
#!/usr/bin/env bash
S=myapp
DIR=~/code/myapp
# 会话已存在就直接进入
tmux has-session -t "$S" 2>/dev/null && exec tmux attach -t "$S"
tmux new-session -d -s "$S" -n code -c "$DIR" # 窗口 1:code
tmux split-window -h -t "$S:code" -c "$DIR" -l 40% # 右侧开 40% 宽的 pane
tmux send-keys -t "$S:code.2" 'pnpm dev' Enter # 在右侧 pane 启动服务
tmux new-window -t "$S" -n logs -c "$DIR" # 窗口 2:logs
tmux select-window -t "$S:code"
tmux select-pane -t "$S:code.1"
exec tmux attach -t "$S"

定制自己的快捷键

用 bind 绑定按键,-r 表示可连按:按一次前缀后,在 repeat-time 时间内可以连续按同一个键。

~/.tmux.conf
# 前缀 < / >:当前窗口左移 / 右移一位,可连按
bind -r < swap-window -d -t -1
bind -r > swap-window -d -t +1
# 前缀 Tab:回到上一个窗口(补回被 l 占用的功能)
bind Tab last-window

改完按 前缀 r 重新加载。加之前先用 前缀 ? 看一下这个键有没有被占用。

远程服务器上的 tmux

在服务器上跑长任务时,先 ssh 登录,再在服务器上开 tmux。网络断了,任务也不会中断,重新登录后 tmux attach 就能回来。

本地 tmux 里再 ssh 进远程 tmux,就是「嵌套」:按一次 Ctrl-a 只有本地 tmux 收到。我的配置有 bind C-a send-prefix,所以连按两次 Ctrl-a,就会把前缀发给里层的 tmux。

附录:我的完整配置

上文提到的「我的配置」就是下面这份。插件由 tpm 管理,第一次使用要先装 tpm,再在 tmux 里按 前缀 I 安装插件。

~/.tmux.conf
set -g default-terminal "screen-256color"
set -as terminal-features ",xterm-256color:RGB"
unbind C-b
set -g prefix C-a
bind C-a send-prefix
set -g base-index 1
set -g pane-base-index 1
set -g renumber-windows on
set -g history-limit 100000
set -sg escape-time 10
set -g focus-events on
set -g mouse on
set -g set-clipboard on
setw -g mode-keys vi
bind r source-file ~/.tmux.conf \; display-message "tmux config reloaded"
bind c new-window -c "#{pane_current_path}"
bind '"' split-window -v -c "#{pane_current_path}"
bind % split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
bind \\ split-window -h -c "#{pane_current_path}"
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5
bind -T copy-mode-vi v send-keys -X begin-selection
bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "pbcopy"
set -g status-position bottom
set -g status-justify left
set -g status-left-length 30
set -g status-right-length 120
set -g status-left "#[fg=green]#S "
set -g status-right "#[fg=cyan]%Y-%m-%d #[fg=white]%H:%M "
set -g @plugin "tmux-plugins/tpm"
set -g @plugin "tmux-plugins/tmux-sensible"
set -g @plugin "tmux-plugins/tmux-yank"
set -g @plugin "tmux-plugins/tmux-resurrect"
set -g @plugin "tmux-plugins/tmux-continuum"
set -g @continuum-restore "on"
run "~/.tmux/plugins/tpm/tpm"

延伸阅读:tmux wiki · tmux-resurrect · tmux-continuum