本文会介绍 tmux 的基础使用方法,以及长期使用下来的一些常用配置。
背景 & 原理
在使用命令行的过程中,我们经常遇到一个需求,那就是让程序在退出当前终端后保持运行。在以往,我们可能会使用 nohup
加让程序后台运行的方式,比如:
➜ ~ nohup ping 127.0.0.1 &
[1] 89083
nohup: ignoring input and appending output to 'nohup.out'
➜ ~ ps -elf |grep ping
0 S yfy 89083 88672 0 85 5 - 3352 - 11:39 pts/0 00:00:00 ping 127.0.0.1
这样在断开终端连接后,重新登陆到服务器,可以发现 ping 进程仍然在运行。nohup 默认将程序输出追加到 nohup.out
文件中,可以通过 tail 检验这一点。
➜ ~ tail -f nohup.out
64 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.024 ms
64 bytes from 127.0.0.1: icmp_seq=8 ttl=64 time=0.027 ms
64 bytes from 127.0.0.1: icmp_seq=9 ttl=64 time=0.023 ms
64 bytes from 127.0.0.1: icmp_seq=10 ttl=64 time=0.035 ms
➜ ~ ps -elf |head -n1
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
➜ ~ ps -elf |grep ping
0 S yfy 89083 1 0 85 5 - 3352 - 11:39 ? 00:00:00 ping 127.0.0.1
正常情况下,当终端退出时(如关闭终端窗口,或者 ssh 网络中断),shell 进程会给所有子进程发送一个 SIGHUP 信号,从而导致进程结束。而 nohup 可以让进程忽略掉 SIGHUP 信号,从而在后台保持运行。
Tip: ps 输出还可以观察到 ping 进程父进程变为了 1,这是因为原本的父进程 shell 已经退出,Linux 内核会将孤儿进程交由 init/systemd 进程托管。
nohup 在简单场景比较有用,但是不适用更复杂的场景。比如用户无法再对后台进程进行输入操作,并且同时维护多个进程也很不方便。不过我们其实还有更优雅、更强大的方案,那就是 tmux。
tmux 是一个终端复用器(Terminal MUltipleXer)。简单来说,tmux 的功能可以概括成一句话:在一个终端里创建多个窗口,并且将窗口分割成多个窗格,每个窗格都运行独立的 shell 进程。这样你就可以让多个应用程序同时运行,而无需打开多个终端模拟器窗口(terminal emulator,如 Xshell、PuTTY、Windows Terminal 等)。下图展示了使用 tmux 创建了一个窗口,并将窗口上下分屏成两个窗格,每个窗格都运行一个 zsh。
更重要的是,tmux 将终端和会话(session)进行了分离。我们把创建的窗口和窗格(和其中创建的所有 shell 进程及子进程)看成一个会话,用户可以随时退出(detach)当前会话,会话会在服务器后台保持运行。在之后,用户可以重新连接上(attach)这些会话,从而继续之前的工作。
原理上,tmux 采用了客户端和服务器分离的架构(C/S 架构)。用户看到和交互的是 tmux client,tmux client 中创建的窗口(和 shell 进程)连接在 tmux server 进程上。因此,当用户退出 tmux client 后,会话内容可以在后台运行。
通过 pstree 命令,可以看到上图中运行的两个 zsh 的父进程是 tmux: server
。而和用户交互的其实是 tmux: client
,其下面并无其他子进程。
➜ ~ pstree -u yfy|less
sshd---zsh---tmux: client
tmux: server-+-zsh-+-less
| `-pstree
`-zsh---ping
在简单了解了下 tmux 的原理后,马上开始 tmux 的上手体验吧。tmux 基础使用非常简单,花 10 分钟就可以完全了解。
tmux quick start
大部分 Linux 发行版都提供 tmux
包,可以通过包管理器安装。其他安装方式(如编译安装)也可以参考官方文档:Installing · tmux/tmux Wiki。
在安装 tmux 后的第一步,我们首先创建一个 tmux 会话。运行 tmux
命令后,会进入 tmux 并创建第一个窗口,在这里你可以像往常一样执行命令。
tmux
接着我们介绍最重要的退出话会话和重连会话的方法。按下 Ctrl-B
后,再按 d
可以退出(detach)当前会话。这样会回到输入 tmux 前的 shell。
通过 tmux list-sessions
或者 tmux ls
可以查看当前所有的会话信息。
➜ ~ tmux ls
0: 1 windows (created Thu Jul 3 14:06:05 2025)
通过 tmux attach
可以连接回第一个会话。
tmux a # attach
接下来介绍 tmux 最关键的多窗口管理功能,有了它就可以大大提升多任务操作的效率。 tmux 有 session(会话), window(窗口), pane(窗格)三个粒度。session 是 tmux 最大的一个粒度,session 下可以创建多个 window,window 可以分割成多个 pane。这里重点介绍窗口和窗格相关的快捷键。
窗口操作
# 创建
Ctrl-B c # 创建新的窗口
Ctrl-B & # 删除当前窗口
# 切換
Ctrl-B Tab # 切换到刚刚的窗口
Ctrl-B p # 切换上一个
Ctrl-B n # 切换下一个
Ctrl-B 数字编号 # 切换到指定一个窗口
# 修改窗口名字
Ctrl-B , # 修改当前窗口名字
窗格操作
# 创建
Ctrl-B \" # 上下切分
Ctrl-B % # 左右切分
Ctrl-B x # 删除
# 切换
Ctrl-B 方向键 # 方向键上下左右
Ctrl-B [hjkl] # 使用 vi 风格的 hjkl 键切换,分别对应左上下右
Ctrl-B z # 切换全屏
相信你操作了这些快捷键后,已经可以感觉到 tmux 的方便和强大了。tmux 也是一个像 vim 那样灵活性很高的软件,很多时候一个你不知道的快捷键就能极大体验。因此需要去更多尝试。tmux list-keys
可以查看 tmux 的所有快捷键绑定。
本文只介绍了 tmux 的快捷键使用方法,tmux 的软件设计非常简洁高效,以上所有快捷键都有对应它的一个子命令,比如垂直划分窗格对应 tmux split-window -v
命令。如果记不住快捷键,可以直接使用 tmux 命令。tmux man 手册的 COMMAND
章节提供了所有命令的详细解释。另外本文没有介绍 tmux 的 vi copy 模式,使用该模式可以方便查找、复制程序输出内容,该内容比较进阶,感兴趣的读者可以自行了解。
常用配置分享
tmux 像 vim 一样支持高度定制化,可以通过修改 ~/.tmux.conf
配置各种快捷键,网络上也有 oh-my-tmux 这样的项目来帮你做一些配置。接下来分享一些我长期使用下来,感觉很有必要的配置,完整配置见我的 github 仓库。
Bonus:在
~/.bashrc
或~/.zshrc
中设置alias t="tmux"
,tmux attach 效率立刻提升 300%!
t # 创建 sessin
t a # attach 到第一个 session
修改快捷键前缀
Ctrl-B
按起来距离比较远,很不方便。可以修改成 Ctrl-X
。
# prefix
set -g prefix C-x
unbind-key C-b # disable default prefix
bind C-x send-prefix
划分窗格快捷键
窗格左右划分和上下划分快捷键比较难记。修改为下划线 _
是上下分屏,-
是左右分屏。
# @ pane
# split current window horizontally
bind _ split-window -v -c "#{pane_current_path}"
# split current window vertically
bind - split-window -h -c "#{pane_current_path}"
鼠标操作
tmux 默认没有启用鼠标,导致无法使用鼠标滚动历史记录。启用鼠标后,还可以直接点击切换不同窗格,以及拖动选择文字复制。
set-option -g mouse on # open mouse scroll
打开窗口时默认路径
tmux 打开新窗口时,shell 的默认路径是启动 tmux 客户端时的路径。可以通过以下配置实现:
Ctrl-X Alt-C
:更改默认路径为当前路径- 创建 panel 时,使用当前路径(
-c
参数)
bind M-c attach-session -c "#{pane_current_path}" # Alt-C, to change current path
# @ pane
# split current window horizontally
bind _ split-window -v -c "#{pane_current_path}"
# split current window vertically
bind - split-window -h -c "#{pane_current_path}"
移动窗口顺序
Ctrl-Shift
加方向键左右,可以调整 window 的顺序。
bind -n C-S-Left swap-window -t -1\; select-window -t -1
bind -n C-S-Right swap-window -t +1\; select-window -t +1
session 操作
虽然 window 和 pane 已经足够进行多任务管理了。但是 tmux 提供的多 session 操作也有其用处。适合管理若干完全不相关的任务。
修改快捷键,使其和 window 快捷键类似:Ctrl-C
创建新 session,N
/P
(大写)切换上一个和下一个 session。
# create session
bind C-c new-session
# session navigation
# (/) #move to prev/next session
bind -r BTab switch-client -l # move to last session
bind -r N switch-client -n
bind -r P switch-client -p
复制文字后不自动跳到结尾
tmux 鼠标选中文字会自动复制,但是会自动跳到结尾。有时在查看一些很长的输出历史时,并不想回到结尾,可以通过以下配置禁用跳转:
# copy select text, and don't jump to end
# https://stackoverflow.com/questions/32374907/tmux-mouse-copy-mode-jumps-to-bottom
bind -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-selection
鼠标选中文字后会进入 tmux 的 copy 模式,无法输入命令,此时可以按
q
退出。
tmux in tmux
有时候想要在 tmux 内再连接另一台服务器,然后也使用 tmux。正常情况下 tmux 快捷键只会传递给外层的 tmux 因此无法工作,但是可以使用以下配置实现按 F12 在内外 tmux 间切换。
# 按 F12 切换到内嵌 tmux,在 macos 里需要系统设置中取消 F12 占用
# 1. prefix 为 None,不再拦截快捷键
# 2. key-table 为 off,下面再绑定 off 下的 F12,使之能退出内嵌模式
# 3. 改变 statusbar 颜色,以便知道已进入内嵌模式
# 4. 如果处于特殊模式,退出
unbind -T root F12
bind -T root F12 \
set prefix None \;\
set key-table off \;\
set status-style bg=colour235 \;\
if -F '#{pane_in_mode}' 'send-keys -X cancel' \;\
refresh-client -S
# 在 off 表里绑定 F12,恢复之前的设置,以退出该模式
bind -T off F12 \
set -u prefix \;\
set -u key-table \;\
set -u status-style \;\
refresh-client -S
另一个小技巧是:连续按两次 Ctrl-B
时快捷键会传递给内部 tmux。