Tmux 会话持久化:Resurrect 与 Continuum 配置

Tmux 会话持久化:tmux-resurrect + tmux-continuum

概述

通过 tmux-resurrect + tmux-continuum 实现:

  • 手动自动 保存完整的 tmux 环境(会话、窗口、面板、布局、工作目录)
  • 重启系统后 自动恢复 tmux 会话
  • 可选:恢复面板中 正在运行的程序

基本配置

~/.tmux.conf 中添加:

set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'

# 每 15 分钟自动保存
set -g @continuum-save-interval 15

# 启用自动恢复(tmux 启动时自动恢复上次会话)
set -g @continuum-restore on

# 启用开机自启(通过 systemd)
set -g @continuum-boot on

安装后 prefix + I 拉取插件,然后 tmux source-file ~/.tmux.conf 重载。

保存文件位置

~/.local/share/tmux/resurrect/
├── tmux_resurrect_20260719T164714.txt   # 历史保存文件
├── tmux_resurrect_20260720T104253.txt
├── ...
└── last -> tmux_resurrect_20260720T233636.txt  # 符号链接指向最新保存

可通过 set -g @resurrect-dir '/custom/path' 自定义保存目录。

保存文件格式

每行用 tab 分隔,行首标识类型:

pane 行

pane    {session}   {window}    {active}    {flags} {pane_idx}  {title} :dir    {is_active} {command}   :{full_command}
  • {command} — tmux 的 #{pane_current_command},即面板中运行的程序名
  • {full_command} — 保存策略捕获的完整命令行(以 : 开头,空则为 :

window 行

window  {session}   {idx}   :name   {active}    :flags  {layout}    {:auto_rename}

state 行

state   {client_session}    {client_last_session}

关键机制

自动保存流程

  1. continuum.tmux 被加载时,将 continuum_save.sh 注入到 status-right
  2. 每隔 @continuum-save-interval 分钟触发一次保存
  3. save.sh 收集所有面板信息并写入保存文件
  4. 如果与上次内容相同则跳过(不更新 last 符号链接),避免无意义的文件增长

自动恢复流程

  1. tmux 服务启动时,continuum.tmux 检测 just_started_tmux_server
  2. 调用 continuum_restore.sh(后台执行,延迟 1 秒等待插件加载)
  3. 检查 @continuum-restore on 且无其他 tmux 服务器在运行
  4. 调用 restore.sh 重建会话、窗口、面板
  5. 恢复窗口布局、标题、活动面板等

系统自启动(systemd)

@continuum-boot on 时:

  1. tmux 启动时自动创建 ~/.config/systemd/user/tmux.service
  2. 运行 systemctl --user enable tmux.service
  3. 系统启动时通过 systemd 启动 tmux(new-session -d
  4. tmux 启动后自动触发恢复

程序恢复

默认行为

默认只恢复以下程序:

vi vim view nvim emacs man less more tail top htop irssi weechat mutt

自定义恢复列表

# 增加更多程序
set -g @resurrect-processes 'ssh psql mysql sqlite3'

# 程序名包含参数时需要引号
set -g @resurrect-processes 'some_program "git log"'

# 恢复所有程序(谨慎使用)
set -g @resurrect-processes ':all:'

# 不恢复任何程序
set -g @resurrect-processes 'false'

高级匹配语法

语法 说明
~program 模糊匹配,program 出现在命令行任意位置即匹配
program->cmd 替换恢复命令(原本的 program 替换为 cmd 执行)
cmd * 保留原始参数
~rails->rails server * 组合:匹配 rails,用 rails server + 原参数恢复

示例:

# 匹配 'rails server' 的完整路径,恢复时用简洁命令
set -g @resurrect-processes '"~rails server->rails server *"'

Vim/Neovim 会话恢复

tmux-resurrect 内置 vim/nvim 会话策略。恢复时自动查找并加载 Session.vim 文件。

需要配合 vim 的 session 插件(如 tpope/vim-obsession 或 neovim 的 :mksession)。

保存策略对比

策略 文件 原理
ps (默认) ps.sh ps -ao ppid,args 查找 pane PID 的直接子进程
pgrep pgrep.sh pgrep -lf -P $PID 查找直接子进程
linux_procfs linux_procfs.sh 通过 /proc 读取,与 ps 类似
deep_leaf (自定义) deep_leaf.sh tty + 进程树深度搜索,更准确

deep_leaf 策略详解

问题:默认 ps 策略只能捕获 pane PID 的直接子进程。如果 shell 中运行前 台命令,该命令是 shell 的直接子进程,可以正常捕获。但如果有进程嵌套 (如 npm startnode server.js),或 shell 有多个子进程,可能捕获错误。

解决方案deep_leaf 策略通过以下步骤精确找到前台命令:

  1. 获取 pane PID 所在的 tty(如 pts/1
  2. 列出该 tty 上所有处于前台进程组(stat 含 +)的进程
  3. 过滤掉 shell 进程(zsh/bash/fish 等)
  4. 选择离 pane PID 最近的进程
  5. 读取 /proc/PID/cmdline 获取完整命令行

回退机制:如果 tty 方式失败(如 tty 为 ?),则递归搜索所有后代进程, 找到最深的非 shell 叶子进程。

常见问题与解决

1. 重启后会话无法恢复

原因排查

  • 检查 @continuum-restore on 是否设置
  • 检查 ~/.local/share/tmux/resurrect/last 符号链接是否有效
  • 手动执行恢复脚本测试:~/.tmux/plugins/tmux-resurrect/scripts/restore.sh
  • 确认没有 ~/tmux_no_auto_restore 停用文件

如果希望开机自动启动 tmux:确保 @continuum-boot on 已设置,且 systemd 服务已启用(systemctl --user is-enabled tmux.service)。

2. 恢复后每个面板显示 %

这是 zsh 的 PROMPT_SP(部分行标记)。当终端输出不以换行结尾时,zsh 显示 % 提示。

解决:在 ~/.zshrc 中添加:

1
unsetopt PROMPT_SP

3. 恢复后程序/命令丢失

根本原因:tmux-resurrect 只能保存和恢复面板的初始进程(通过 pane_full_command 字段)。对于从 shell 中交互式启动的命令:

  • 如果命令是 shell 的直接子进程且在前台运行 → 保存策略可以捕获
  • 如果保存时面板空闲(没有前台命令)→ pane_full_command 为空,无法恢复
  • 如果命令进程嵌套多级 → 需要用 deep_leaf 策略

最佳实践

  • 对于需要持久化的服务,使用 tmux new-window 'command' 直接创建
  • 配合 vim/nvim 的 session 机制
  • 使用 @resurrect-processes ':all:' 搭配 deep_leaf 策略

4. continu.tmux 中有 set -x

插件文件 continuum.tmux 第 3 行如果有 set -x,会导致每次加载时输出 大量 bash 调试信息。应删除该行。

5. 保存文件目录权限

保存目录 ~/.local/share/tmux/resurrect/ 需要有读写权限。 如果目录不存在或不可写,保存会静默失败。

6. 切换默认 shell 后的兼容性

如果从 fish 切换到 zsh(或其他 shell),旧保存文件中的 pane_command 仍然 显示为 fish。但这不影响恢复,因为恢复时使用当前的 default-shell 配置。 只需更新 ~/.tmux.conf 中的 set -g default-shell /usr/bin/zsh

手动操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 手动保存
~/.tmux/plugins/tmux-resurrect/scripts/save.sh

# 手动恢复
~/.tmux/plugins/tmux-resurrect/scripts/restore.sh

# 查看最新保存内容
cat ~/.local/share/tmux/resurrect/last

# 查看所有保存文件
ls -la ~/.local/share/tmux/resurrect/

# 验证 systemd 服务状态
systemctl --user status tmux.service
systemctl --user is-enabled tmux.service

调试技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 检查当前配置
tmux show-option -gqv @continuum-restore
tmux show-option -gqv @continuum-boot
tmux show-option -gqv @resurrect-save-command-strategy
tmux show-option -gqv @resurrect-processes

# 测试保存策略(替换 PANE_PID 为实际值)
bash ~/.tmux/plugins/tmux-resurrect/save_command_strategies/deep_leaf.sh $PANE_PID

# 获取面板 PID
tmux display-message -p -F '#{pane_pid}' -t {session}:{window}.{pane}

# 查看面板进程树
pstree -p $(tmux display-message -p -F '#{pane_pid}' -t {session}:{window}.{pane})

Tmux 会话持久化:Resurrect 与 Continuum 配置
https://tingfeng347.github.io/2026/03/09/Tmux 会话持久化:Resurrect 与 Continuum 配置/
作者
Tingfeng
发布于
2026年3月9日
许可协议