tmux 从入门到精通:会话托管与远程运维实战
在通过 SSH 管理服务器时,你经常会遇到这样的问题:远程执行编译、数据同步或业务程序等长时间任务时,一旦网络波动或 SSH 连接断开,前台任务会被立即终止。传统解决方案要么依赖 nohup 牺牲交互能力,要么编写 systemd 单元文件来守护进程,前者无法随时查看运行现场,后者对临时任务而言过于繁琐。
tmux(Terminal Multiplexer,终端复用器)是一款开源终端复用工具,它将会话与终端连接解耦:任务运行在由独立服务器进程托管的会话中,SSH 断开只会分离画面而不会中断任务,重新连接后工作现场完整恢复。同时它提供了一套命令行接口,可以在脚本中以无人值守的方式创建、检查和控制会话,因此也常被用作轻量级的服务托管方案。
tmux 的核心优势:
- 会话持久化:进程在后台持续运行,不受 SSH 断开或终端关闭影响。
- 现场可恢复:重新接入后恢复完整的屏幕输出历史,便于排查问题。
- 脚本可控:
new-session、has-session、send-keys等命令均可脚本调用,适合无人值守场景。 - 多任务复用:一个连接内管理多个会话、窗口与分屏窗格。
- 零配置可用:本文所有操作均基于默认配置与默认快捷键,无需修改任何配置文件。
本文将从安装、核心概念、会话管理、脚本化托管、输出查询、窗口窗格、复制模式及常见问题等方面,帮助你系统掌握 tmux 在服务器运维中的使用。
目录
- 1. 引言:什么是 tmux?
- 2. 安装 tmux
- 3. 核心概念:服务器进程与会话分离
- 4. 会话管理基础
- 5. 脚本化托管:无人值守运行业务程序
- 6. 输出查询与格式化
- 7. 窗口与窗格速查
- 8. 复制模式:滚动回看与搜索日志
- 9. 其他实用功能
- 10. 常见问题
- 11. 总结
- 12. 参考资料
1. 引言:什么是 tmux?
tmux 是一个终端复用器:它允许在单个屏幕上创建、访问和控制多个终端。当 tmux 启动后,会在服务器上常驻一个服务器进程(Server Process),所有会话都由它托管。你与 tmux 的每一次交互都是一次”客户端接入”——断开只是客户端离开,会话本身始终存在于服务器内存中。
这一架构决定了 tmux 的两大典型用途:
- 交互式使用:作为日常终端的工作台,多会话多窗格组织工作现场。
- 脚本化托管:在无人值守的环境(如开机自启)中以受控方式运行业务程序,故障后可随时接入查看。
对于服务器运维场景,第二点尤其有价值:相比 systemd 单元文件,tmux 托管只需几行 Shell 脚本;相比 nohup,它保留了完整的交互能力和屏幕现场。
2. 安装 tmux
本章覆盖各主流环境的安装方法,并确认版本满足要求。
2.1 检查是否已安装
tmux -V # 输出版本号,例如 "tmux 3.2a";提示 command not found 则需安装多数 Linux 发行版默认不带 tmux,但均在官方仓库中提供。
2.2 在主流环境中安装
根据你的环境选择对应命令:
Debian / Ubuntu
sudo apt update && sudo apt install -y tmux # 更新索引并安装 tmuxRHEL / CentOS / Rocky
sudo dnf install -y tmux # RHEL 8+ 使用 dnf;CentOS 7 将 dnf 换成 yumArch Linux
sudo pacman -S tmux # 从官方仓库安装macOS
brew install tmux # 通过 Homebrew 安装提示:本文所有用法在 tmux 2.x 及以上版本均适用。3.2+ 版本额外提供弹出式窗口等新特性,但不在零配置用法范围内。
3. 核心概念:服务器进程与会话分离
理解本章是掌握 tmux 一切用法的基础,尤其是无人值守托管场景。
3.1 会话、窗口与窗格三层结构
tmux 采用三层组织结构:
server(服务器进程)
└── session(会话) ← 托管业务的容器,可命名,可持久存在
└── window(窗口) ← 占满整个终端画面的工作区
└── pane(窗格) ← 窗口内的分割区域,每个窗格是一个独立的伪终端| 层级 | 定位 | 典型用途 |
|---|---|---|
| session | 长期容器 | 一个业务程序一个会话,如 myapp |
| window | 任务分组 | 同一业务的不同工作项 |
| pane | 屏幕分割 | 同时观察多个输出 |
脚本化托管主要操作 session 这一层;交互使用时才会频繁操作 window 与 pane。
3.2 为什么 SSH 断开后任务不会终止
普通终端中运行的前台进程是 SSH 会话的子进程,连接断开时随会话一起收到挂断信号(SIGHUP)而被终止。
tmux 改变了这一归属关系:你在 tmux 中启动的程序,其父进程是 tmux 服务器进程,而非 SSH 连接。tmux 服务器以守护进程形式常驻,与任何 SSH 连接无关。因此:
- SSH 断开 → 仅当前”客户端”分离 → 会话及其中的程序继续运行;
- 重新 SSH 登录后执行
tmux attach→ 客户端再次接入 → 屏幕现场原样恢复。
只要服务器不重启、没有人显式销毁会话,其中的程序就一直运行。这就是无人值守托管的可行性来源。
4. 会话管理基础
本章介绍日常最常用的会话操作:创建、分离、重连、查看与销毁。熟练掌握后,“断线恢复”和”多任务切换”都不再是问题。
4.1 创建命名会话
tmux new -s work # 创建名为 work 的会话并立即接入(new 是 new-session 的别名)-s 参数指定会话名称。命名不是必需的,但在多会话场景下强烈建议:后续所有针对该会话的操作(attach、send-keys、kill)都可以用名字精确指定目标。
创建后底部会出现绿色状态栏,显示当前会话名、窗口列表等信息,表明你现在正处于 tmux 会话内部。
也可以创建后不进入,让它在后台直接运行指定命令:
tmux new -d -s backup "tar czf /tmp/data.tgz /data" # 后台创建会话并立即执行备份命令-d(detach)表示创建后不接入终端。这种”创建即执行”的形式正是第 5 章脚本托管的基础。
4.2 分离与重连
分离是 tmux 最高频的快捷键操作。在会话内部按下:
| 按键 | 作用 |
|---|---|
Ctrl+b 再按 d | 分离当前客户端,回到创建 tmux 之前的终端 |
注意按键节奏:先按 Ctrl+b(松开),再单独按 d。Ctrl+b 称为前缀键(Prefix),详见 7.1 前缀键的使用方式。
分离后,会话与其中的一切继续在后台运行。重连方式:
tmux attach # 接入最近使用的会话(attach 是 attach-session 的别名)
tmux attach -t work # 明确接入名为 work 的会话如果接入时提示会话不存在,先用 tmux ls(见下节)确认现存会话名。
提示:SSH 断线属于被动分离,效果与主动按
Ctrl+b d相同,重连后现场无损恢复。
4.3 查看、销毁与重命名
tmux ls # 列出所有会话(ls 是 list-sessions 的别名)
# 输出示例:work: 1 windows (created Tue Aug 25 10:00:00) (attached)
tmux kill-session -t work # 销毁名为 work 的会话,其中运行的进程一并结束
tmux rename-session -t old new # 将会话 old 重命名为 newtmux ls 输出的关键信息:会话名在前,(attached) 表示当前有客户端接入,没有该标记则表示会话处于后台。
两个补充要点:
- 当最后一个会话被销毁后,tmux 服务器进程自动退出——这是默认行为,此时
tmux ls会报错no server running on /tmp/tmux-0/default,属于正常现象而非故障。 - 会话内部的快捷键操作:
Ctrl+b$重命名当前会话;Ctrl+bs以交互列表切换会话。
5. 脚本化托管:无人值守运行业务程序
本章是全文核心,演示如何用一段可重复执行的 Shell 脚本,把业务程序交给 tmux 托管:开机自动拉起、崩溃后可检测、随时可接入排查。所有命令均来自 tmux 的命令行接口,无需任何人工交互。
5.1 场景与整体思路
典型需求:一台网关服务器上有若干业务程序(如采集网关、守护脚本),要求:
- 开机自启:服务器重启后程序自动运行;
- 幂等:脚本重复执行不会重复拉起多个实例;
- 可观测:随时能接入会话查看程序的实时输出;
- 可恢复:程序崩溃后能重新拉起。
对应的 tmux 能力映射:
| 需求 | 实现命令或组合 |
|---|---|
| 启动并驻留 | tmux new-session -d -s 名称 -c 目录 程序 |
| 幂等防重 | tmux has-session -t 名称 |
| 崩溃重启 | tmux send-keys -t 名称 注入按键 |
| 存活探测 | tmux list-panes -F '#{pane_pid}' 配合 pgrep |
| 人工介入 | tmux attach -t 名称 |
5.2 后台创建会话并运行程序
最小可用的一行托管:
tmux new -d -s myapp -c /opt/myapp "./myapp" # 后台创建会话 myapp,工作目录设为 /opt/myapp 并运行程序三个参数各司其职:
-s myapp:会话命名,后续操作的目标;-c /opt/myapp:设置会话初始工作目录,相对路径、配置文件的读取都以它为基准;"./myapp":在会话的第一个窗口中执行的命令。
提示:把启动命令直接写在
new-session参数里,而不是先创建空会话再输入命令,可以避免 Shell 初始化完成前的时序竞争问题。
验证运行状态:
tmux ls # 能看到 myapp 会话即创建成功
tmux attach -t myapp # 接入查看程序实时输出,Ctrl+b d 分离退出5.3 幂等检查:has-session
has-session 用于判断会话是否存在,它是脚本幂等的关键:
if tmux has-session -t myapp 2>/dev/null; then
echo "session exists" # 会话已存在,退出码 0
else
echo "session missing" # 会话不存在,退出码 1
fihas-session 的行为约定:会话存在返回退出码 0,不存在则打印错误到标准错误流并以退出码 1 退出。2>/dev/null 抑制错误输出,让判断只依赖退出码。据此实现”不存在才创建”:
if ! tmux has-session -t myapp 2>/dev/null; then
tmux new -d -s myapp -c /opt/myapp "./myapp" # 仅当会话不存在时才创建
fi无论这段代码被执行多少次,myapp 都只会有一个实例。
5.4 注入按键:send-keys
当会话还在但里面的程序已经退出时,可以向会话注入按键,如同真人坐在终端前敲键盘:
tmux send-keys -t myapp C-c # 发送 Ctrl+C,确保旧进程收到中断信号
tmux send-keys -t myapp "cd /opt/myapp && exec ./myapp" C-m # 输入启动命令并发送回车-t myapp:目标会话;C-c:表示 Ctrl+C 组合键;C-m:表示回车键(Carriage Return,回车符在 ASCII 中的名称)。
send-keys 把”人敲键盘”这个动作完全脚本化了。配合 has-session 判断,就能区分三种状态分别处理:会话不存在 → 创建;会话存在且程序存活 → 跳过;会话存在但程序已死 → 注入按键重启。
5.5 健康检查:#{pane_pid} 与 pgrep
如何判断”程序是否还活着”?每个窗格内都有一个由 tmux 创建的主进程(通常是 Shell),#{pane_pid} 就是它的 PID:
tmux list-panes -t myapp -F '#{pane_pid}' # 输出该会话第一个窗格主进程的 PID,例如 12345注意:pane_pid 是窗格当前主进程的 PID。未使用 exec 时,它通常是 Shell,业务程序是 Shell 的子进程;使用 exec 后,业务程序会直接替换 Shell,pane_pid 本身就变成业务程序。因此不能只依赖 pgrep -P,还要检查窗格当前命令:
pane_info=$(tmux list-panes -t myapp -F '#{pane_pid}|#{pane_current_command}|#{pane_dead}') # 查询窗格主进程、当前命令和退出状态
IFS='|' read -r pane_pid pane_command pane_dead <<< "$pane_info" # 拆分查询结果
if [[ "$pane_dead" != "1" ]] && { [[ "$pane_command" == "myapp" ]] || pgrep -P "$pane_pid" > /dev/null 2>&1; }; then
echo "running" # exec 模式匹配当前命令,普通 Shell 模式匹配业务子进程
else
echo "stopped"
fi在通用脚本中,应把 myapp 替换成实际的 program_name,并将这段判断封装成函数。程序崩溃退出后,窗格当前命令会回到 Shell,或窗格进入 pane_dead=1 状态,健康检查随之失败。
5.6 完整自启托管脚本示例
将上述能力组装为一个通用的托管函数。以下脚本可直接保存为 start_apps.sh,按需修改 main() 中的业务清单:
#!/bin/bash
# start_apps.sh - 业务程序 tmux 托管脚本:可重复执行,支持幂等创建与崩溃恢复
set -euo pipefail # 任一命令失败、变量未定义、管道中间失败均立即退出
log() {
printf '[%s] ==> %s\n' "$(date '+%Y-%m-%d %H:%M:%S%z')" "$*" # 加入时间戳,便于定位启动顺序与耗时
}
# 判断会话中的业务程序是否正在运行
is_running_in_tmux() {
local session_name="$1"
local program_name="$2"
if ! tmux has-session -t "${session_name}" 2>/dev/null; then
return 1 # 会话不存在,视为未运行
fi
local pane_info pane_pid pane_command pane_dead
pane_info=$(tmux list-panes -t "${session_name}" -F "#{pane_pid}|#{pane_current_command}|#{pane_dead}" 2>/dev/null)
IFS='|' read -r pane_pid pane_command pane_dead <<< "${pane_info}" # 拆分窗格状态
[[ "${pane_dead}" != "1" ]] || return 1 # 窗格主进程已退出,业务视为未运行
[[ "${pane_command}" == "${program_name}" ]] || pgrep -P "${pane_pid}" > /dev/null 2>&1 # 同时兼容 exec 与普通 Shell 模式
}
# 在指定会话中托管指定程序
run_in_tmux() {
local session_name="$1"
local program_path="$2"
if [[ ! -f "${program_path}" ]]; then
log "Program not found: ${program_path}"
return 1 # 程序文件缺失,直接报错
fi
if [[ ! -x "${program_path}" ]]; then
chmod 755 "${program_path}" # 缺少执行权限时自动补齐
fi
local program_dir program_name
program_dir=$(dirname "${program_path}") # 程序所在目录,作为会话工作目录
program_name=$(basename "${program_path}") # 程序文件名
if ! tmux has-session -t "${session_name}" 2>/dev/null; then
# 会话不存在:后台创建并直接运行程序
tmux new-session -d -s "${session_name}" -c "${program_dir}" "./${program_name}"
log "[${session_name}] started."
return 0
fi
if is_running_in_tmux "${session_name}" "${program_name}"; then
log "[${session_name}] already running, skip." # 已在运行,跳过保证幂等
return 0
fi
# 会话存在但业务已死:注入按键重新拉起
tmux send-keys -t "${session_name}" C-c
local launch_command
printf -v launch_command 'cd -- %q && exec %q' "${program_dir}" "./${program_name}" # 转义路径,避免空格等字符破坏命令
tmux send-keys -t "${session_name}" "${launch_command}" C-m
log "[${session_name}] restarted."
}
main() {
run_in_tmux 'myapp' '/opt/myapp/myapp' # 业务网关程序
run_in_tmux 'guard' '/opt/myapp/bin/guard.sh' # 守护脚本
}
main "$@"脚本的决策流程:
run_in_tmux(名字, 程序路径)
├─ 程序文件不存在? ── 是 → 报错返回
├─ 会话不存在? ──── 是 → new-session -d 创建并运行
├─ 业务进程存活? ── 是 → 打印 skip,跳过(幂等核心)
└─ 其余情况 → send-keys 注入 Ctrl+C 和启动命令,重新拉起5.7 接入开机自启
托管脚本本身是普通 Shell 脚本,交给系统的自启机制执行即可。两种常用方式:
方式一:crontab @reboot
crontab -e # 编辑当前用户的定时任务,添加下面一行@reboot /opt/scripts/start_apps.sh 2>&1 | /usr/bin/logger -t start_apps@reboot 表示每次系统启动时执行一次。logger 将脚本输出写入系统日志,由 journald 或系统日志服务统一记录时间并负责轮转,避免直接追加 /var/log/start_apps.log 后不断膨胀。不同发行版的 logger 路径可能不同,可先用 command -v logger 确认并替换 /usr/bin/logger。
查看本次启动的自启日志:
journalctl -t start_apps -b # 查看本次启动以来的全部脚本输出
journalctl -t start_apps -b -e # 查看本次启动日志的末尾
journalctl -t start_apps --since today # 查看今天的脚本输出提示:
@reboot只负责在开机时调用一次脚本,不负责持续拉起崩溃进程;如果需要自动恢复,应使用 systemd 服务的Restart=能力,或让脚本由周期任务重复执行。
方式二:systemd 一次性服务
# /etc/systemd/system/start-apps.service
[Unit]
Description=Start business apps in tmux sessions
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot # 一次性服务:执行完脚本即退出
ExecStart=/opt/scripts/start_apps.sh
RemainAfterExit=yes # 执行完成后标记为 active,便于 systemctl status 查询
[Install]
WantedBy=multi-user.target # 多用户模式启动阶段触发sudo systemctl daemon-reload && sudo systemctl enable --now start-apps.service # 加载配置并启用systemd 会自动收集 ExecStart 的标准输出和错误输出,日志自带时间戳,可用以下命令查询:
journalctl -u start-apps.service -b # 查看本次启动的服务日志
systemctl status start-apps.service # 查看服务状态及最近日志两种方式都只在开机时调用一次脚本;运行期的崩溃恢复由脚本自身的幂等逻辑承担。若需要持续监控和自动重启,建议将业务程序直接建模为 systemd 服务,使用 Restart=on-failure;tmux 更适合作为需要保留交互现场的托管层。
5.8 手动拉起崩溃的业务进程
除了 send-keys,tmux 还提供了更直接的窗格重建命令 respawn-pane,适合人工介入时使用:
tmux respawn-pane -k -t myapp "./myapp" # 强制重启窗格并在其中重新运行程序
# -k 表示先杀掉窗格内现有进程;不指定命令时仅重启默认 Shell与 send-keys 方案的区别:respawn-pane 直接替换整个窗格进程树,动作更彻底,适合 Shell 本身已被污染或退出的场景;send-keys 则保留了原 Shell 环境,适合常规重启。
6. 输出查询与格式化
脚本化运维不仅要”控制”,还要”查询”。tmux 的 list-* 命令配合 -F 格式化参数,可以把会话与窗格的状态输出成任意格式,供监控脚本消费。
6.1 list-sessions 与 list-panes
tmux list-sessions # 所有会话概览
tmux list-sessions -F '#{session_name}' # 只输出会话名,每行一个
tmux list-panes -t myapp -F '#{pane_pid} #{pane_current_command}' # 指定会话内每个窗格的 PID 与当前命令
tmux list-panes -a -F '#{session_name}:#{pane_pid}' # -a 遍历服务器上所有会话的所有窗格-F 参数接受一个格式字符串,其中 #{变量} 会被替换为对应属性值。这让 tmux 可以像 ps、jq 一样成为监控流水线的数据源:
for s in $(tmux list-sessions -F '#{session_name}' 2>/dev/null); do
echo "session: $s" # 遍历所有会话名,逐个输出
done6.2 常用格式化变量
运维场景高频使用的格式化变量如下:
| 变量 | 含义 |
|---|---|
#{session_name} | 会话名称 |
#{session_created} | 会话创建时间戳(Unix 秒) |
#{session_attached} | 接入该会话的客户端数量 |
#{window_name} | 窗口名称 |
#{window_index} | 窗口编号 |
#{pane_pid} | 窗格主进程 PID |
#{pane_current_command} | 窗格中当前前台进程的命令名 |
#{pane_current_path} | 窗格内进程的当前工作目录 |
#{pane_dead} | 窗格内进程是否已退出(1 为已死) |
#{pane_id} | 窗格唯一 ID(形如 %0,生命周期内不变) |
组合示例——一条命令输出全部会话的健康概况:
tmux list-panes -a -F '#{session_name} | pid=#{pane_pid} | cmd=#{pane_current_command} | dead=#{pane_dead}'
# 输出示例:myapp | pid=12345 | cmd=./myapp | dead=07. 窗口与窗格速查
接入会话排查问题时,窗口与窗格用于组织多个视图(如一边看业务输出、一边看系统日志)。本章以速查表形式列出默认快捷键,不做展开。
7.1 前缀键的使用方式
tmux 的快捷键统一采用两段式:先按前缀键 Ctrl+b(松开),再按功能键。例如分离会话是 Ctrl+b 然后 d,而不是三个键同时按。
如果想让 Ctrl+b 本身穿透到应用程序(例如在程序里也需要 Ctrl+b),按 Ctrl+b 再按 Ctrl+b 即可发送一次原始按键。
7.2 快捷键速查表
以下均为 tmux 默认绑定,无需任何配置即可使用:
| 类别 | 按键 | 作用 |
|---|---|---|
| 会话 | Ctrl+b d | 分离当前会话 |
| 会话 | Ctrl+b s | 交互式列出并切换会话 |
| 会话 | Ctrl+b $ | 重命名当前会话 |
| 会话 | Ctrl+b ( / ) | 切换到上一个 / 下一个会话 |
| 窗口 | Ctrl+b c | 新建窗口 |
| 窗口 | Ctrl+b , | 重命名当前窗口 |
| 窗口 | Ctrl+b w | 交互式列出并切换窗口 |
| 窗口 | Ctrl+b & | 关闭当前窗口 |
| 窗口 | Ctrl+b 数字 | 跳转到对应编号窗口 |
| 窗口 | Ctrl+b p / n | 上一个 / 下一个窗口 |
| 窗格 | Ctrl+b " | 上下分屏 |
| 窗格 | Ctrl+b % | 左右分屏 |
| 窗格 | Ctrl+b 方向键 | 按方向移动焦点 |
| 窗格 | Ctrl+b o | 焦点轮换到下一个窗格 |
| 窗格 | Ctrl+b q | 显示窗格编号,快速按数字跳转 |
| 窗格 | Ctrl+b x | 关闭当前窗格 |
| 窗格 | Ctrl+b z | 最大化 / 还原当前窗格(zoom) |
| 窗格 | Ctrl+b Space | 循环切换预设布局 |
| 窗格 | Ctrl+b Ctrl+方向键 | 调整窗格大小(步进 1 行列) |
| 窗格 | Ctrl+b Alt+方向键 | 调整窗格大小(步进 5 行列) |
| 窗格 | Ctrl+b { / } | 与前一个 / 后一个窗格交换位置 |
| 窗格 | Ctrl+b ! | 将当前窗格拆分为独立窗口 |
| 通用 | Ctrl+b ? | 列出全部默认快捷键 |
| 通用 | Ctrl+b : | 进入命令模式,直接输入 tmux 命令 |
| 通用 | Ctrl+b f | 在所有窗口中搜索文本 |
提示:
Ctrl+b?是内置的帮助手册。忘记按键时优先查它,不必依赖外部资料。
8. 复制模式:滚动回看与搜索日志
tmux 窗格的历史输出默认保存在回滚缓冲区中(行数上限为 2000 行)。要向上翻阅历史输出——例如查看程序早些时候的报错——需要进入复制模式(Copy Mode)。这是排查问题时最高频的交互操作。
8.1 进入与退出
| 操作 | 按键 |
|---|---|
| 进入复制模式 | Ctrl+b [ |
| 进入并直接上翻一页 | Ctrl+b PageUp |
| 退出复制模式 | q 或 Esc |
进入复制模式后,窗格右上角会显示当前位置指示器(如 [0/123]),表示当前浏览位置与缓冲区总行数。
8.2 导航与翻页
复制模式下的导航按键(默认 emacs 风格):
| 按键 | 作用 |
|---|---|
↑ ↓ | 逐行滚动 |
PageUp / PageDown | 整页翻动 |
Alt+↑ / Alt+↓ | 半页翻动 |
Ctrl+↑ / Ctrl+↓ | 逐行滚动(等效方向键) |
g(Alt+<) | 跳到缓冲区最顶部(最早输出) |
G(Alt+>) | 跳回缓冲区最底部(最新输出) |
排查长输出的推荐动线:Ctrl+b PageUp 进入并上翻 → PageUp 连续翻页定位 → G 一键回到最新输出。
8.3 搜索历史输出
复制模式内支持增量搜索,比手动翻页高效得多:
| 按键 | 作用 |
|---|---|
Ctrl+s | 向下增量搜索(从当前位置向缓冲区底部) |
Ctrl+r | 向上增量搜索(从当前位置向缓冲区顶部) |
输入关键词后即时跳转匹配位置,重复按 Ctrl+s 或 Ctrl+r 跳到下一个匹配项,Esc 结束搜索留在当前位置。
此外还有两个不进入复制模式的搜索入口:
Ctrl+bf:跨所有窗口搜索文本,结果以交互列表展示,点击直接跳转;- 命令模式中执行
show-messages可查看 tmux 自身的消息历史。
8.4 选择、复制与粘贴
复制模式内的文本选取(默认 emacs 风格):
| 步骤 | 按键 | 作用 |
|---|---|---|
| 1. 开始选择 | Ctrl+Space | 在光标处设置选区起点 |
| 2. 移动光标 | 方向键 / 搜索 | 选区随光标扩展 |
| 3. 复制 | Alt+w | 复制选区到 tmux 粘贴缓冲区,并退出复制模式 |
| 4. 粘贴 | Ctrl+b ] | 将最近一次复制的内容粘贴到当前窗格 |
补充说明:
- 复制的内容存放在 tmux 自己的粘贴缓冲区中,
Ctrl+b=可交互式列出历史缓冲区并选择粘贴;Ctrl+b#列出全部缓冲区。 - 默认情况下 tmux 缓冲区与系统剪贴板相互独立:从 tmux 复制的内容不一定能在外部粘贴。零配置下最简单的绕行方案是用终端模拟器自带的选择复制(鼠标拖选后中键或
Shift+Insert),它作用于终端渲染层,与 tmux 无关。 - 若需把大段日志导出分析,更推荐 9.1 记录窗格输出到文件 的方式,比手动选取可靠。
9. 其他实用功能
本章收录两个低频但价值明确的运维技巧。
9.1 记录窗格输出到文件
pipe-pane 可以把窗格的一切输出实时管道给外部命令,最典型的用法是落盘成日志:
tmux pipe-pane -t myapp -o 'cat >> ~/myapp-console.log' # 开启记录:窗格输出持续追加到日志文件-t myapp:目标会话(省略时作用于当前窗格);-o:切换开关语义——再执行一次同样的命令即关闭记录;'cat >> 文件':把输出原样追加写入指定文件。
排查偶发问题前提前打开记录,事后就能拿到完整的时间线现场。注意该记录从开启时刻起生效,不会补录历史内容。
9.2 多终端共享同一会话
同一个会话允许多个客户端同时接入,所有客户端看到完全相同的画面与输入:
tmux attach -t myapp # 在另一个 SSH 终端执行同样命令,两端画面实时同步典型场景:两名运维同时登录同一账号排障,一人操作另一人实时观看;或者本机终端与手机 SSH App 同时接入,随时随地盯着同一个任务。
提示:以上指同一系统用户下的共享。跨用户共享涉及套接字权限变更,超出默认行为范围,生产环境不建议开放。
10. 常见问题
10.1 提示 no server running on /tmp/tmux-…
错误提示:
no server running on /tmp/tmux-1000/default原因:当前没有任何存活的会话。tmux 默认在最后一个会话销毁后自动退出服务器进程,此后所有查询类命令都会报此错误。
解决:这不是故障。执行 tmux new -d -s 会话名 创建新会话即可,服务器进程随之自动拉起。
10.2 SSH 断开后任务到底有没有在跑?
原因:取决于任务是在哪里启动的。tmux 会话内启动的任务由 tmux 服务器托管,断线不影响;直接在裸终端(未进入 tmux)启动的任务仍属于 SSH 会话,断线即被终止。
解决:养成习惯——凡是长任务,先进 tmux 再执行。不确定时用 tmux ls 查看会话是否存在,tmux attach -t 名称 接入确认。
10.3 会话还在,但 attach 后一片空白或程序不见了
原因:业务进程崩溃退出后,窗格内只剩空闲 Shell;极端情况下 Shell 也退出,窗格随之关闭。
解决:tmux attach -t 会话名 接入后观察 Shell 是否存活;程序退出则按 5.8 手动拉起崩溃的业务进程 用 respawn-pane 重建,或用 send-keys 重新执行启动命令。
10.4 鼠标滚轮无法回看历史输出
原因:tmux 默认未接管鼠标,滚轮事件由本地终端处理,只能翻动终端自身有限的回滚行数,翻不到 tmux 缓冲区里的更早输出。
解决:使用 8. 复制模式:滚动回看与搜索日志 的键盘操作回看,Ctrl+b PageUp 进入翻页模式是最常用的入口。
10.5 中文显示乱码
原因:服务器或客户端的 locale 未设置为 UTF-8,导致多字节字符被错误解码。 解决:确认环境变量并保持客户端与服务端一致:
echo $LANG # 查看当前 locale,期望输出类似 zh_CN.UTF-8 或 en_US.UTF-8
export LANG=zh_CN.UTF-8 # 临时设置当前 Shell 的 localetmux 2.2 及以上版本默认启用 UTF-8 支持,无需额外设置;极老版本的注意事项可忽略。
10.6 忘记了某个操作怎么按
解决:Ctrl+b ? 列出全部默认快捷键绑定;Ctrl+b : 进入命令模式后输入 list-commands 可列出全部命令。所有本文提到的行为都能在这两个内置帮助中找到出处。
11. 总结
tmux 是一款轻量而强大的终端复用器,通过”服务器进程托管会话”的核心机制,解决了远程运维中连接易断、现场难留、进程难管的三大问题。它既是一个交互式的终端工作台,也是一套完备的脚本化托管接口——new-session 负责拉起,has-session 负责幂等,send-keys 负责恢复,list-panes -F 负责观测,四者组合即可替代繁琐的守护进程配置。
掌握 tmux 后,你可以:
- 在几百台服务器上用同一段脚本无人值守地托管业务程序,SSH 断线不再是事故。
- 随时接入任意会话查看实时输出,用复制模式和搜索快速定位历史日志。
- 用会话、窗口、窗格组织多任务工作现场,在一台跳板机上并行处置多起事件。
- 把 tmux 的查询接口接入自己的监控脚本,实现会话级的健康巡检。
建议从第 4 章的会话管理开始上手练习,再逐步实践第 5 章的托管脚本,最终形成适合自己环境的运维范式。