tmux这玩意儿,我用了快十年了。说实话,刚接触那会儿觉得它学习成本高得离谱,完全理解不了为什么有人愿意在终端里“套娃”。但现在如果有人让我退回不用tmux的裸终端,我大概会直接拒绝干活。这篇就聊透tmux,从它到底解决什么痛点,到怎么配置成真正顺手的工具,再到那些文档里不写、得踩过坑才明白的细节。适合所有需要长时间泡在终端里的开发者、运维同学,也适合刚听说tmux但不知道怎么用的小白。
1. 为什么你需要一个终端复用器
先说个场景。你SSH到一台服务器,跑着一个数据迁移脚本,预计要跑两个小时。你盯着屏幕看了半小时,觉得无聊,想断开SSH回家看,但又怕进程被中断。或者更常见的情况:你在自己的电脑上开了好几个终端窗口,每个窗口跑着不同的项目,切换来切换去,窗口越堆越多,标签页乱成一团,不小心关掉一个还得回忆里面跑的是什么。
tmux就是冲着这两个痛点来的。它帮你把所有终端会话“托管”在后台,让你随时脱离、随时回来,就像把工作台整个搬走,下次回来原样摆好。而且它原生支持把一个终端窗口拆成多个面板、多个标签页,管理效率比裸终端高出一个量级。
我有个很强烈的建议:如果你每天要花超过半小时在终端里做事情,tmux会是你今年回报率最高的一个工具投资。不是“学了可能有用”,是“学了就会忍不住天天用”。它跟那些花哨的终端美化不一样,tmux解决的是实打实的工作流问题:会话保持、并发任务、远程协作、上下文切换。
需要说明的是,tmux并不是唯一的选择。老牌的GNU Screen也有类似能力,我自己早年也用过几年。但tmux在架构上更清晰,配置文件更友好,社区生态也活跃得多,插件、主题、脚本一应俱全。除非你有特殊原因离不开Screen,否则我建议直接把精力投给tmux。
2. 核心概念:会话、窗口、窗格
很多人学tmux卡住,是因为没弄懂它三层嵌套的结构。我第一次用的时候也懵,这些名词听着像同义词,实际是完全不同的层级。
先打个比方。你把tmux想象成一个终端管理器,它管理着:
- 会话(Session):一个独立的工作环境。你可以同时开好几个会话,每个会话互不干扰,就像你同时开着好几个独立的“工作台”。
- 窗口(Window):会话里面的标签页。一个会话里可以开多个窗口,每个窗口相当于一个独立的终端页面。
- 窗格(Pane):窗口里被拆分成的小块。一个窗口可以左右、上下拆成多块,每块跑着不同的命令,就像你同时开了好几个分屏。
这三层的关系很直观:
tmux └── 会话(Session)—— 相当于一个工作区 └── 窗口(Window)—— 相当于标签页 └── 窗格(Pane)—— 相当于分屏块绝大多数tmux操作,都是在跟这三个层级打交道。你新建一个会话、切换会话;在一个会话里新建窗口、切换窗口;在某个窗口里拆分窗格、切换窗格。搞清楚了这三层,tmux的基本概念就通了一半了。
另一个核心概念是前缀键(Prefix)。tmux为了保证你的普通终端快捷键不被占用,所有快捷指令都需要先按一个前缀组合键,再按功能键。默认前缀是Ctrl+b。比如你要新建窗口,不是直接按某个键,而是先按Ctrl+b,再按c。
注意:
Ctrl+b这个组合键很多人用不惯,特别是笔记本上没有单独的Ctrl键、或者用小拇指按Ctrl很吃力的情况。后面会讲怎么改成Ctrl+a或者其他顺手的位置。
前缀键机制设计得很巧妙。它相当于给tmux划了一块专用的“指令区”,不会跟终端里其他程序的快捷键冲突。代价就是,所有操作都得练成“先按前缀,再按动作”的肌肉记忆。这段时间忍一忍,用顺手之后你根本不会在意这一步。
会话的脱离与恢复是tmux最核心、最不可替代的功能,值得单独拎出来说。你在某个会话里跑着服务,按下Ctrl+b然后按d,这叫detach(脱离)。会话不会退出,而是转入后台继续运行。你关掉终端也好,断掉SSH也好,对tmux会话没有任何影响。等你想回来做事了,重新打开终端,输入tmux attach,之前的工作现场原封不动地还原在你面前。
这个能力在SSH场景下体验尤其明显。以前我在某台云主机上做部署,最怕跑长任务时网络闪断。断一次就得重新SSH上去,查进程、看日志、再跑一次,折腾死人。用了tmux之后,网络闪断对我完全无感,重新连上之后tmux attach,一切都还在。后来我甚至养成了习惯,在本地长时间编译、跑测试,也丢到tmux会话里,笔记本合上就走人,回来打开终端接着看结果。
3. 安装与基础配置:半小时上手
安装没什么好说的,各类系统都有包管理。
- Ubuntu/Debian:
sudo apt install tmux - CentOS/RHEL:
sudo yum install tmux或sudo dnf install tmux - macOS:
brew install tmux - Windows:如果你用WSL,那在WSL里装就行;纯Windows建议直接用Windows Terminal配WSL用,别折腾原生版本。
装完之后,输入tmux就直接进入一个新会话。这时候屏幕底部会有一条绿色的状态栏,告诉你当前有哪些窗口、系统时间等信息。你按下Ctrl+b,再按%(也就是Shift+数字5那个键),窗口就左右拆成了两块;按Ctrl+b,再按",就上下拆成两块。
这几个基础操作够你撑起一天的基本使用了:
| 操作 | 快捷键 |
|---|---|
| 进入/新建会话 | tmux |
| 脱离会话 | Ctrl+bd |
| 新建窗口 | Ctrl+bc |
| 切换窗口 | Ctrl+b数字 |
| 左右拆分窗格 | Ctrl+b% |
| 上下拆分窗格 | Ctrl+b" |
| 切换窗格 | Ctrl+b方向键 |
| 关闭窗格/窗口 | exit |
| 查看所有会话 | tmux ls |
| 恢复会话 | tmux attach -t 会话名 |
我第一次看到这个表的时候,心想就这?这也不复杂啊。确实,基础操作不复杂,把一个会话拆成几个窗格,跑几个服务,这就算初步会用了。真正的功力在配置和脚本化上。
配置文件在~/.tmux.conf。没有就创建一个。这个文件是tmux的“总控台”,键位映射、外观样式、鼠标行为、插件加载,全在这里定义。
注意:配置文件改完之后,不会自动生效。你可以在命令行执行
tmux source-file ~/.tmux.conf,让配置立即加载;或者按下前缀键后输入:source-file ~/.tmux.conf。
下面这份是我自己用了很久的基础配置,谈不上花哨,但每一项都踩过坑、验过时间:
# 修改前缀键为 Ctrl+a(如果你用不惯 Ctrl+b 可以试这个) unbind C-b set -g prefix C-a bind C-a send-prefix # 用 r 重新加载配置 bind r source-file ~/.tmux.conf \; display-message "配置已重载" # 开启鼠标支持 set -g mouse on # 窗口编号从1开始,前缀键后的数字直接对应窗口编号 set -g base-index 1 setw -g pane-base-index 1 # 状态栏右侧显示时间 set -g status-right "%H:%M" set -g status-interval 60 # 历史滚动缓冲区调大 set -g history-limit 10000解释几个关键项。
改前缀键为Ctrl+a,很多人可能不理解。默认Ctrl+b的位置比较远,特别是你习惯用小拇指按Ctrl的话,这个组合手指很难受。Ctrl+a在标准键盘上就在Caps Lock旁边,按起来轻松得多。但这会带来一个副作用:终端里用Ctrl+a来跳行首的习惯(比如bash、zsh下)会被tmux截胡。不过你想想,你用Ctrl+a跳行首的频率,应该远没有你切换终端窗口的频率高。反正我自己改过去之后,没有遇到过什么不便。
开启鼠标支持,这里要辩证地看。开了之后,你可以直接用鼠标点窗格、拖拽调整大小、滚动历史缓冲区,对新手特别友好。但开了鼠标之后,选中文本复制会变得不顺手,因为你一拖鼠标,tmux会认为你在操作窗格而不是在选文字。我个人的建议是:前期开着,降低学习成本;等键位操作练熟了,再考虑关掉还是用脚本做更精细的鼠标处理。
历史缓冲区调大,这个细节很重要。默认的2000行,跑个构建任务日志就刷没了。调到10000以上,平时排查问题翻日志就方便多了。需要注意的是,历史缓冲区大小是窗口创建时就确定的,改配置只对新开的窗口生效。如果你改完配置发现老窗口还是2000行,那不是配置错了,是新窗口才按新配置走。
配置完这几项,刷新配置文件,tmux的日常体验已经比其他任何终端方案都舒服了。接下来可以往高阶走。
4. 会话管理:多任务的层次感
tmux的基础会话操作你可能已经摸到了,真正让人产生“回不去”感觉的,是它把会话管理的层次感做得很清晰。
你可以给会话命名,然后随时在多个会话之间切换。比如我通常会这样组织:
- 会话
code:跑代码编辑器相关的前端开发服务 - 会话
ops:连着服务器,跑部署、日志监控 - 会话
notes:临时记录用的草稿环境
每次进入tmux,tmux ls看一眼有哪些会话在跑,tmux attach -t ops进入运维会话。退出也不用担心丢东西,一切都在。
几个关键命令:
tmux new -s ops # 新建名为ops的会话 tmux new -s code -d # 新建名为code的会话,但不进入(后台运行) tmux ls # 列出所有会话 tmux attach -t ops # 进入ops会话 tmux kill-session -t ops # 删除ops会话 tmux switch -t ops # 已经在tmux里时,直接切换到某个会话这里有个很实用的技巧:tmux new -s code -d这种后台创建会话的方式,特别适合开机自启或者脚本部署。你可以把一组服务全部丢进tmux后台会话里跑,需要看的时候attach进去看,不需要的时候也不占你前台的终端。
会话里再配一套快捷键,切换就不用敲命令了:
# 前缀键之后按s,预览所有会话列表 # 前缀键之后按(或),在会话之间循环切换Ctrl+b s会弹出一个会话列表,方向键选择、回车进入。这个列表还有个隐藏作用:显示每个会话里开了哪些窗口。有一次我要找之前某次调试用的终端,不记得会话名,就靠Ctrl+b s扫一眼列表,按窗口内容认出来的。这种“管不到底但是能找到”的感觉,用别的工具很难复制。
会话命名有个细节值得提:名字一定要起得有区分度。session-1、session-2这种命名,时间一长根本分不清里面跑的什么。用code、ops、db-migrate这种一眼能认出来的名字,能省掉大量“attach错会话”的时间。
5. 窗口与窗格操作:把终端变成工作台
接下来是最爽的部分:窗口和窗格的组合操作,能把一个物理终端变成一张可以任意排版的工作台。
假设你在跑一个后端服务,需要一边看日志一边调配置。用窗口的方式,你可以一个窗口开vim改代码,另一个窗口跑重启命令;用窗格的方式,你可以把这两个任务同时摆在眼前,左边改代码,右边看日志。我个人更习惯窗格的方式,因为不用来回切换窗口,视野里同时有“改了什么”和“造成了什么结果”,排查问题效率高很多。
窗格拆分的快捷键再回顾一下:
Ctrl+b % 当前窗格右侧拆出新窗格(左右分布) Ctrl+b " 当前窗格下方拆出新窗格(上下分布) Ctrl+b 方向键 切换到某个方向的窗格 Ctrl+b 空格 在窗格布局之间循环切换 Ctrl+b z 最大化/还原当前窗格Ctrl+b z这个功能我第一次用的时候惊到了,它能把当前窗格瞬间占满整个窗口,再按一次还原。这在窗格开太多、某个窗格太小看不清内容的时候特别管用。比如我开了上下左右四个窗格,日志窗格太小,用z放大看一眼,看完再还原,来回两次操作,非常顺滑。
窗格的尺寸调整,默认情况下你可以用前缀键加方向键来调整,但方向键的步进比较粗,不够精细。如果你开着鼠标,直接拖动窗格边框就行,这是最直观的方式。拖动的时候注意:tmux的窗格边框会有一个细高亮线,拖到位后松开即可。
窗口操作也有几个常用快捷键:
Ctrl+b c 新建窗口 Ctrl+b 数字 跳到对应编号窗口 Ctrl+b p 上一个窗口 Ctrl+b n 下一个窗口 Ctrl+b , 重命名当前窗口 Ctrl+b w 窗口列表这里有个使用习惯我特别建议养成:给窗口重命名。tmux默认窗口名是根据里面运行的程序自动变的,比如跑vim了窗口名自动变成vim,跑tail了变成tail。自动命名看着方便,但如果你一个窗口里跑过多个命令,最后它显示的名字不一定是当前状态。手动按Ctrl+b ,,给窗口起个自己能看懂的名字,比如部署、日志、压测,状态栏一目了然。尤其在多窗口场景下,这个习惯能救命。
关于窗格数量,我自己的经验是:一个窗口超过4个窗格就开始显得拥挤。真有更多任务并行需求,开多个窗口比塞在同一个窗口里更清晰。窗格适合“强相关、需同时看”的任务;窗口适合“弱相关、只要来回切”的任务。这个判断标准,你可以根据自己的使用习惯慢慢找到平衡点。
还有一个经常被忽略但实际很好用的功能:Ctrl+b !可以把当前窗格“拆成”一个独立窗口。有些时候你窗格开多了,后来发现某一个窗格的任务已经跟其他窗格不太相关了,用这个快捷键把它独立成窗口,窗格界面立即清爽,而任务还在继续跑。
6. 复制模式与文本选择:在终端里高效“选字”
终端里的复制粘贴,一直是个老大难问题。鼠标一选中文字就触发tmux的窗格操作,根本选不出来。tmux自带的复制模式可以解决这个问题。
先把原理说清楚。tmux的复制模式本质上是让你进入一个“冻结”的浏览视图,在这个视图里,上下滚动、左右移动都不会触发任何程序,你可以自由翻历史输出,然后用键盘或者鼠标选中文本,存进tmux自己的缓冲区(也叫粘贴缓冲区)。之后在任意位置按Ctrl+b ],就能把缓冲区内容粘贴出来。
进入复制模式的快捷键是Ctrl+b [。进入之后,你就可以用方向键、PageUp/PageDown翻看当前窗口的完整历史输出了。选中文本的方式跟vim很像:
Ctrl+b [ 空格或回车开始选择 移动光标选择区域 Alt+w 复制选中内容 Ctrl+b ] 粘贴如果你开着鼠标,更简单的办法是直接用鼠标选中文本,然后再按y或者Enter(取决于tmux版本和配置)确认复制。建议先选好区域,确认内容无误再确认复制,避免复制到一半的错误内容。
需要多说一句的是,tmux复制的东西默认只存在当前tmux会话里,不是系统剪贴板。在SSH场景下,这个问题不大,因为你的tmux会话本来就“住”在服务器上,复制到tmux缓冲区已经够用了。但在本地终端里用tmux,你可能会想把内容复制到系统剪贴板,方便粘贴到浏览器、聊天工具里。这需要用到tmux和系统剪贴板的桥接。
在macOS上,我用的方案是安装reattach-to-user-namespace这个工具,然后在配置文件里绑定复制快捷键时调用它。Linux上则可以用xclip或wl-copy,配合set -g @copy_command 'xclip -selection clipboard'之类的配置。这样按一下复制,内容直接进系统剪贴板,跟普通终端复制粘贴没区别。
注意:不同版本的tmux,复制模式下的快捷键命名略有差异。旧版本用的是
copy-mode-vi这类配置项,新版统一成了key-table和bind-key。如果照着网上老教程改了配置但没生效,可以先查一下你的tmux版本,大概率就是新旧语法不兼容导致的。
我自己实际用的复制键位映射是这样配的:
# 开启vim风格复制模式 setw -g mode-keys vi # 在复制模式下,y键直接复制并退出复制模式 bind-key -T copy-mode-vi y send -X copy-pipe-and-cancel "xclip -selection clipboard" # 用鼠标选中的文本直接复制到系统剪贴板 bind-key -T copy-mode-vi MouseDragEnd1Pane send -X copy-pipe-and-cancel "xclip -selection clipboard"这样配置之后,在复制模式里按y,内容就直接到系统剪贴板了。鼠标选中再松手,也会自动复制。这个配合“鼠标模式开启”的状态下使用,日常复制粘贴体验跟普通终端几乎没差。
7. 脚本化与自动化:让tmux变成流程工具
tmux不只是一个交互工具,你还真的可以用它来编排整套工作流。写一个脚本,启动一个命名好的会话,自动创建窗口、拆分窗格、填充命令,甚至自动执行某些任务。这就像给你的终端环境写了一份“启动计划书”。
最简单的例子。我每次接手一个新项目,第一步就是在tmux里搭好三窗格布局:左边vim,右上跑测试,右下跑git状态。以前手动拆,得敲一串快捷键;现在写个脚本:
#!/bin/bash # 创建名为dev的会话,如果已存在就不重复创建 tmux has-session -t dev 2>/dev/null if [ $? != 0 ]; then tmux new-session -d -s dev -n editor tmux send-keys -t dev 'vim' C-m tmux split-window -h -p 30 tmux send-keys -t dev:1.2 'npm run test:watch' C-m tmux split-window -v -t dev:1.2 -p 30 tmux send-keys -t dev:1.3 'git status' C-m fi tmux attach -t dev这个脚本的核心逻辑:检测dev会话是否已存在,不存在才从头创建,存在就直接attach进去。这样你的“工作台”是幂等的,重复执行也不会开一堆重复会话。
关于send-keys,它是tmux脚本化的核心功能。最简单的用法:tmux send-keys -t 会话名 '命令' C-m,C-m就是回车键。你可以往任何窗口、任何窗格发送任何按键序列。这在自动化部署、预先启动服务、甚至做“定时任务”的时候特别有用。
更进阶的玩法是把tmux脚本和服务器部署结合起来。比如某个服务需要在服务器上常驻,但又希望每次发布都能看日志、重启之后还能查看历史状态。写一个部署脚本:
tmux kill-session -t myservice 2>/dev/null tmux new-session -d -s myservice tmux send-keys -t myservice 'cd /data/app && docker-compose up -d' C-m sleep 3 tmux send-keys -t myservice 'docker-compose logs -f --tail=200' C-m这样服务在后台跑,日志实时滚动,想检查状态就tmux attach -t myservice,看完再Ctrl+b d离开。比用nohup加日志文件的管理方式直观多了。
写tmux脚本时有个坑:new-session命令的场景参数和send-keys的目标参数必须对得上。比如tmux new-session -d -s myservice创建会话后,该会话默认下只有一个窗口,编号是0。但如果你配置里设置了base-index 1,那窗口编号就从1开始。写脚本之前先tmux ls看一眼实际编号,别想当然。
还有一个自动化技巧:利用tmux setenv设置会话级别的环境变量。这可以保证某个会话里所有窗口、窗格都共享同一份环境变量,而不污染全局。比如某个服务的API地址,在某个会话里设成测试地址,另一个会话设成正式地址,互不干扰。
.tmux.conf里也能配置“启动即执行”的钩子,比如这个配置片段:
# 进入某会话后自动执行的命令 set-hook -g session-opened "if-shell -F '#{==:#{session_name},dev}' 'run-shell \"echo \\\"欢迎回来\\\\n\\\"\"'"不过说实话,这个钩子的写法和调试复杂了点,新人先别碰,等基础操作熟练了、确实有需求再研究不迟。我自己真正常用到的就是send-keys加split-window的组合,已经覆盖了90%的场景。
8. 外观定制与其他技巧:把状态栏变成信息面板
tmux默认的外观很多人觉得丑,绿色的状态栏、单调的字体,看着就有“远古工具”的味道。但tmux的可定制程度非常高,状态栏可以显示时间、负载、电池、git分支,甚至天气预报等任意信息,只要你愿意写脚本去对接数据源。
先看一个最基础的定制思路:修改状态栏颜色和布局。
# 状态栏左半部分 —— 显示会话名和窗口列表 set -g status-left "#[fg=black,bg=green] #{session_name} #[default]" set -g status-left-length 20 # 状态栏右半部分 —— 显示时间 set -g status-right "#[fg=black,bg=yellow] %H:%M:%S #[default]" # 状态栏前景/背景色 set -g status-style "bg=colour236, fg=colour231"状态栏右侧的时间可以精确到秒,用%H:%M:%S就行。这在跑长任务的时候很有用,看一眼状态栏就知道跑了多久了。
窗口列表的显示格式也值得研究:
# 当前窗口高亮显示,其他窗口变灰 setw -g window-status-current-style "fg=black,bg=green" setw -g window-status-style "fg=colour244,bg=colour236" # 窗口名左右加空格,视觉上更清晰 setw -g window-status-format " #I:#W " setw -g window-status-current-format " #I:#W "#I是窗口编号,#W是窗口名。默认的窗口状态样式没什么间距,多个窗口挤在一起看着费劲,加上空格之后舒服很多。
tmux插件的生态也很值钱。最有名的插件管理器是[tmux-plugins/tpm],它让你像装软件包一样装tmux扩展。常用的插件:
tmux-resurrect:保存/恢复整个tmux环境,重启机器后一键还原tmux-continuum:定时自动保存tmux状态,崩溃了也能恢复tmux-yank:增强复制功能,直接复制到系统剪贴板tmux-battery:状态栏显示电量
以tmux-resurrect为例,这插件简直是我的保命符。有一次我的电脑系统更新后自动重启,所有终端会话都断了,正常情况意味着一堆进程和上下文全没了。但因为我装了tmux-resurrect,重启电脑、打开终端、prefix + Ctrl+s保存状态,prefix + Ctrl+r恢复状态,所有的窗口、窗格、当前目录、甚至vim打开的缓冲区和滚动历史,全部原样回来了。那种“毁机不毁工作”的安心感,别的工具很难给到。
安装tpm插件的方式很简单,在配置文件底部加几行:
# 安装tpm(只需一次) git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm # 配置文件里声明要装的插件 set -g @plugin 'tmux-plugins/tmux-resurrect' set -g @plugin 'tmux-plugins/tmux-continuum' set -g @plugin 'tmux-plugins/tmux-yank' # 初始化tpm run '~/.tmux/plugins/tpm/tpm'然后重新加载配置,按前缀键加I(大写I),插件就会自动下载安装。装完之后的功能自己试试就知道了。
外观定制最后提一点:tmux颜色方案最好是跟你的终端配色保持协调。如果你用的终端字体和背景色偏暗色主题,tmux状态栏也可以调成暗色系。相比白得刺眼、绿得老气的默认样式,调完之后整个终端工作区看起来舒服很多。配色这个东西没有绝对标准,我自己的原则是状态栏颜色不要比终端主区域更显眼,避免喧宾夺主。
9. 边缘场景与网络异常处理
tmux用的越深,越会遇到一些“非典型”情况。我整理一下自己踩过的几个坑,以及对应的处理方案。
场景一:网络闪断后会话还活着,但看着像卡住了
SSH断线重连、重新tmux attach之后,整个界面有时候会短暂地卡住不动,输入命令也没反应。这其实是终端回显状态没完全恢复,尤其在你的网络抖动、重连过程中,终端尺寸跟之前不一致时容易触发。解决办法很简单:随便按一个键(比如方向键或者回车)让它重新绘制,或者执行tmux resize-window -A,让tmux按当前终端尺寸重新计算布局。如果你给窗口改过背景色、启用了某些颜色主题,在所有重连之后都可能遇到类似情况,重新绘制一下就好。
场景二:attach的时候提示“sessions should be nested with care, unset $TMUX to force”
这个警告出现的情况是:你在一个tmux会话里,试图再attach另一个tmux会话。tmux不是 запрещ这操作,但它会提示你要小心。实际上嵌套tmux会话在多数情况下根本不需要,你要做的是在已有会话里tmux switch -t 目标会话切换,而不是再次attach。如果你确有特殊需求非得嵌套,那就在命令行临时设置unset TMUX之后再attach。不过这种情况我几乎没遇到过,99%的场景用switch就够。
场景三:已经启动了某个长任务,但忘了放进tmux会话里
这种是最让人抓狂的。SSH连上去直接跑了个大任务,跑了一半,想起来没开tmux,这怎么办?答案是:没法直接“捞”到tmux里。但有一个变通方案:用reptyr把现有进程重新挂到一个新的tmux会话下。步骤如下:
tmux new-session -d -s rescue tmux switch -t rescue # 在新的会话里 reptyr 进程PIDreptyr的原理是把当前终端的所有权和控制权转让给目标进程,相当于把正在裸跑的进程“收养”进当前tmux环境。这个工具不是tmux自带的,需要额外安装。但那个“任务忘开tmux”的懊恼时刻,它真的能救人。
场景四:系统重启后,所有tmux会话都没了
tmux本身不带“开机自动恢复”的能力,但我前面已经提到了tmux-resurrect加tmux-continuum组合拳。tmux-continuum是每15分钟自动保存一次tmux状态,tmux-resurrect是手动或自动恢复。配置好之后,系统重启的损失几乎为零。另外一个不太起眼但很实用的功能是:tmux-resurrect可以恢复“面板里当前运行的命令”以及“vim打开的文件”,所以你在某个窗格里正在编辑的代码文件,恢复之后也能回到你离开时的状态。
场景五:tmux会话里某个窗格卡死,无法响应命令
如果某个窗格运行了失控命令,卡死不动,你又不想结束整个会话,可以用Ctrl+b x关闭当前窗格。这会直接“杀死”窗格里的进程。如果这招都不管用,还能用Ctrl+b &关闭整个窗口,当然要先确认窗口里没有什么重要的后台任务。这操作有些暴力,建议是先看一眼窗格标题或者跑个ps确认一下进程状态再动手。
10. tmux与编辑器、SSH的搭配经验
聊完tmux本身的细节,还要说说它跟其他工具的组合用法。tmux的价值不是孤立的,它配合vim、配合SSH、配合现代终端,才能发挥出真正的效率。
tmux与vim的组合。
很多vim重度用户会用tmux来管理多项目、多vim实例。这里有一个后患无穷的操作习惯要提醒你:别在tmux里把vim的分屏和tmux的窗格混用。二者可以混,但混乱起来极为痛苦。我自己踩过的坑:在tmux的一个窗格里打开vim,然后又在vim里split出两个vim窗口,这时候再用tmux拆分窗格、切换窗格,整个键盘操作就乱了套,光标到底在哪个层级里活动你都搞不清楚。
我的建议是:用tmux承担“窗格管理”,用vim承担“文件内编辑”。一个项目一个tmux会话,会话里开几个窗口,每个窗口里跑一个独立的vim实例,每个vim实例保持单屏编辑。偶尔在vim里做个横向分屏看两个文件还好,但别超过一层嵌套。保持边界清晰,操作就不会乱。
另外vim和tmux之间的复制粘贴体验,建议专门配置一下。vim复制的时候直接写入系统剪贴板,tmux也写系统剪贴板,这样两端复制的内容是互通的。vim里配置set clipboard=unnamedplus,然后在tmux里用我前面提到的xclip桥接,两端复制基本无感。
tmux与SSH的搭配。
如果只是单机使用tmux,你只需要记住一个会话名;一旦跟SSH结合,会话名的组织就显得很重要。我的习惯是:每台远程服务器上的tmux会话名,跟服务器用途挂钩。比如服务器是用来跑部署的,会话名就叫deploy;是用来看日志的,就叫logs。这样在本地电脑上也能通过tmux attach -t deploy直接进入远程服务器(需要先ssh登上去再说)。如果多台服务器之间还要做批量操作,会话命名的统一性更加重要。
在远程服务器上使用tmux还有一个值得推荐的小技巧:给.bashrc或.zshrc末尾加上一行自动恢复逻辑。
if [[ -z "$TMUX" ]] && [[ -z "$SSH_CONNECTION" ]]; then tmux attach -t default 2>/dev/null || tmux new -s default fi这段逻辑的意思是:当你在本地打开终端时,如果已经有tmux会话default存在,就自动attach;没有就新建一个名为default的会话。这样你的终端一打开就自动进入专属工作环境,免去了手动输入tmux attach的步骤。不过这个配置只适合本地终端,不适合SSH进去的场景。SSH进去建议还是手动决定要不要进入会话,毕竟有时候就只是上去执行一条命令而已,没必要套一层tmux。
tmux与终端模拟器的配合。
现阶段的终端模拟器功能越来越强,比如标签页、分屏、多面板,很多人问:那还需要tmux吗?我的回答是:终端模拟器的分屏只解决“视觉切分”,解决不了“会话保持”的问题。终端模拟器的标签页关了就是关了,进程就没了;tmux会话是怎么折腾都不会丢的。更何况终端模拟器的分屏在SSH场景下完全无效,你在本地开好分屏、SSH到服务器,分屏布局跟远程环境毫无关系。tmux的价值恰恰在于“会话在远端”,跟本地窗口布局解耦。所以两套工具并不冲突,终端模拟器负责本地窗口管理,tmux负责远端会话管理,各管一段。
如果你用的是macOS上的iTerm2,可以设置一个快捷键:打开iTerm2自动执行tmux attach或tmux new,配合上述的判断逻辑,实现“打开终端就回到工作现场”。Linux上用Terminator、Windows Terminal也能做到类似效果,原理是一样的。
11. 关键排查:几个容易踩的配置误区
写到这,tmux的主体内容已经够全面了。但这部分我想专门聊几个配置阶段的高频坑。有很多人拿着网上的tmux配置,结果跑不起来或者行为怪异,来问我排查了半天,最后问题往往都出在几个相似的点上。
配置文件语法错误是最常见的原因。
tmux的配置语法在不同版本之间有细微差别,网上很多教程又老又新混着抄,照着配很容易出现“命令无法识别”报错。比如早期版本用set-option、set-window-option这些长命令,新版本虽然兼容但已经推荐短写法set、setw。如果你看到unknown command之类的报错,逻辑上应该先把配置拆成最小单元,逐行注释掉,看哪一行引发了问题。
还有一个常见的坑:配置文件里引号不匹配或者中文引号混进去了。尤其是复制网上配置时,某些平台会把英文引号自动转成中文引号(“”),tmux解析不了这种全角符号,立刻报错。我数不清帮人排查过多少次了,一切语法问题先从这找起。
鼠标复制和滚动的冲突。
开启set -g mouse on之后,你可能会发现:在某个命令的输出特别长、想用鼠标往上翻的时候,直接在窗格里滚轮,内容却不动。这是tmux把滚轮事件截获走了。解决办法:在tmux里用滚轮或触控板滚动,需要先进复制模式;或者按住Shift再用鼠标滚轮,部分终端会临时把操作“让渡”给终端模拟器。这个行为有时候让人抓狂,所以在“鼠标支持”和“文本选择/滚动”之间,你需要根据自己的使用习惯做取舍。我自己后来在配置文件里绑定了一个快捷键,用Ctrl+b m来切换mouse on/off,需要纯鼠标操作时关掉tmux的鼠标接管,完事后重新打开。
窗口号从0还是从1开始,影响脚本和直觉。
很多教程建议设置base-index 1让窗口编号从1开始,因为从0开始太不符合直觉。这本身没问题,但如果你在脚本里写死了tmux select-window -t 0这种命令,而会话里实际窗口编号是1,命令就会失败。
状态栏刷新频率影响系统负载。
tmux默认每15秒刷新一次状态栏,这个频率一般够用。有些人的配置里为了显示负载、电量等信息,把status-interval改成了1秒。这在小负载场景下没感觉,但在服务器上开着十几个tmux会话、每个都1秒刷一次,就可能会产生无谓的系统调用,加大CPU负载。状态栏信息的即时性一般不需要精确到秒,刷新频率设成5秒或15秒都够。
不同终端的转义序列问题,导致窗格乱画。
如果你在某个终端里正常,换到另一个终端后tmux窗格边框乱掉、显示错乱,多半是终端的转义序列支持不一致。解决办法是调整tmux对终端能力的检测,比如在配置里设置set -g default-terminal "screen-256color"或"tmux-256color"。不同系统的终端可能有差异,值班时多试几个候选值。另外,把SSH客户端的SendEnv和AcceptEnv相关配置放宽,确保TERM变量能正确传达,也能缓解这类问题。
12. 实战工作流:我的一天tmux使用范本
讲了这么多配置和原理,最后做一次“实战走场”,让大家看看tmux在实际工作里长什么样。
早上到工位,打开终端,执行tmux attach。因为我在.bashrc里配了自动恢复逻辑,终端直接就回到了我最常用的ops会话,左边窗格是实时日志,右边窗格是服务监控面板,中间窗格是shell,可以随时敲命令。昨晚的服务状态一目了然,不用重新执行一堆命令。
开始写代码时,Ctrl+a c新建窗口,命名成code,在里面打开vim,开始写一个接口逻辑。写到一半需要查看数据库里的数据,Ctrl+a %把窗口左右拆成两半,右边窗格用mysql查数据,左边继续写代码。查完数据,Ctrl+a o把光标切回左边窗格,继续vim。整个过程行云流水,完全没有“新开终端”“切换窗口”的打断感。
下午要发布一个服务,Ctrl+a c新建窗口deploy,输入部署命令。发布过程中日志滚得飞快,怕漏掉关键信息,按Ctrl+a [进入复制模式,往上翻日志,找到报错的时间点,按空格选中、回车复制,再粘贴到IM工具里发给同事分析。全程不用退出tmux,不用在一个窗口和另一个窗口之间来回拖信息。
发布完之后,Ctrl+a d脱离会话,直接关掉终端。晚上回家想起来了,打开笔记本,SSH到服务器,tmux attach -t ops,看到下午那个发布的服务还在跑着呢,日志窗口已经滚了上万行。这种“工作现场全天待命”的体验,一旦习惯了,真就回不去了。
如果你刚开始用tmux,我的建议是一次只学一个新功能。第一周只用会话的创建和恢复,第二周开始用窗口,第三周再碰窗格。别一上来就把所有快捷键背完、把所有插件装完,那样只会把自己淹没在“配置工具的配置”里,反而学不会工具本身。先用一个月,把基础操作练成肌肉记忆,再逐步扩展。
另外多说一句,tmux跟你系统的包管理器关系很密切。Linux服务器上,有些精简镜像是没有tmux的,用的时候发现tmux: command not found,先别慌,apt install tmux装一下就好。但如果你的环境不允许装新软件,那倒是真巧了,你又回到了需要自己实现会话保持的状态。这时候可以用screen顶一下,或者写个nohup加日志的替代方案。工具嘛,永远是服务目标的,不是目标本身。
tmux的价值不在它本身,在于你从此拥有了一个可以随时离开、随时回来的终端工作台。它给工作带来的连续性,是任何终端模拟器、任何tab插件都给不了的。希望你也能体会到这种“怎么折腾都不乱”的掌控感。