Skip to content

tmux

tmux是一个终端复用工具,她可以在一个屏幕上创建、访问和控制多个终端。tmux 同样可以从屏幕上分离让终端在后台继续运行。

安装

几乎所有的发行版都存在不同版本的 tmux,使用对应的包管理工具安装即可。当然我们也可以从源码安装

基本概念

tmux 是标准的 C/S 程序。tmux 的所有状态都保存在 tmux server 进程中,他在后台运行并管理 tmux 运行的所有其他进程。

当我们执行 tmux 命令时 tmux server 会自动启动,直到没有在运行的进程时 tmux server 会自动退出。当用户执行 tmux 命令时 tmux client 会接管终端并通过 tmux client 连接到 tmux server ,并使用 /tmp 中的套接字来与 tmux server 进行通信。

tmux 还包含三个核心的概念:

  1. session: 会话,每次执行 tmux 命令都会开启一个会话。同一个会话具有同样地执行环境
  2. windows: 窗口,一个会话中具有多个窗口。窗口会占据整个终端的空间
  3. panes: 面板,一个窗口可以包含多个面板,而组织面板的方式被称为 window layout

每个会话具有同样地执行环境,而窗口是同一个执行环境中的不同任务(例如编辑、运行等),而面板是同一个任务的不同分支。我们执行命令都是在面板层面进行的。第一次运行 tmux 会开启一个会话,这个会话只包含了一个窗口,而这个窗口只有一个面板。

Tips

每次操作都只能在一个面板上进行,该面板被称为 active paneactive pane 所在的窗口被称为 current window。上一个 current window 被称为 last window。很多命令会引用这些术语

会话默认会以 0 1 2... 作为 session name,但是他并不表示索引,会话之间是平等的关系。当然在创建会话是也能够指定名称。

窗口默认会根据创建的先后顺序创建 window index (所以存在 pre/next 关系),窗口默认会以 active pane 中运行的程序名称作为 window name(可以更改, 并且并不要求唯一)。

Tips

面板也有 index 和 title,其中 index 是以顺时针自动编号的(无法更改),切换面板经常会依赖这个编号,而 title 并不是很常用

基础使用步骤

tmux 的使用就是对 session、windows 和 panes 的增删改查。

create session

当用户执行 tmux 命令时就会创建一个会话,它实际上是一个语法糖,完整的命令应该是:

Bash
# tmux / tmux new 都可以
tmux new-session
# 可以使用 -s 来指定名称
# 如果不指定默认名称为 0, 1, 2 ...
tmux new-session -s session_name

创建一个新的会话之后,会自动连接到该会话并创建一个索引为 0 的窗口,该窗口包含一个面板,这个面板中运行了一个 shell。

status line

默认配置下,当我们连接到一个 session 后,会下方显示一个绿色的状态栏,其中包含了一些内容,典型定义:

Bash
# [0]: session name
# 0:man : window index:window name
# - : last window
# * : current window
# "MacBook-Pro" : pane title(默认是运行 tmux 的 hostname)
# 10:33 22-Feb-20 : time date
[0] 0:man 1:bash- 2:bash 3:bash*    "MacBook-Pro"   10:33 22-Feb-20

Tips

tmux 默认风格出了名的丑,不过他提供了强大的配置方式

prefix key

一旦连接到 tmux client,输入的任何按键都会转发到 active pane 中运行的程序上(通常就是 bash 这样的 shell)。因此需要一个特殊的方式来与 tmux 本身进行交互,这就是 prefix key 的意义。

默认的 prefix key<C-b> 。按下 prefix key 后 tmux 会等待另一个按键的按下,然后决定执行什么 tmux 命令。 要想发送 prefix key 本身可以按两次 prefix key

Tips

C-b ? 来查看所有 tmux 绑定的快捷键,在这个帮助面板中默认使用 <up> <down> 来滚动,q 来退出

command prompt

所有的快捷键实际上都映射到一个 tmux 命令上,不过命令必须在 shell 中被执行,如果 active pane 中运行的程序是一个 shell,那么完全可以使用命令来完成 tmux 操作,但是如果运行的时 python 这样的程序,就无法直接与 shell 交互了。

为了解决这个问题,tmux 可以使用 <prefix>: 打开一个交互式命令行(他会出现在状态栏所在的位置),在其中我们可以输入 tmux 命令。不过更多的时候都是使用快捷键操作。

在交互式命令行中不需要输入 tmux,例如在 shell 下创建 session 的命令为 tmux new-session -n mysession 而在交互命令行模式下直接输入 new-session -n mysession 即可。

Tips

可以使用 ; 来执行多个命令

copy mode

tmux 具有自己的 copy 和 paste 系统,它使用 copy mode 来实现,基本的操作方式如下:

  1. 输入 <prefix>[ 进入复制模式
  2. 按下空格开始复制(begin-selection)
  3. 按下回车复制选中文本(copy-selection)并退出复制模式
  4. 按下 <prefix>] 粘贴文本

Tips

复制模式有两种按键形式,一种 emacs 风格,一种 vi 风格

tmux 复制操作的内容默认会存进 buffer 里面,我们可以使用 list-buffers 来查看其中的内容,buffer 最多能够保存 50 条复制信息,如果超过新的会覆盖旧的条目。

可以手动执行 delete-buffer -b <buffer-name> 来删除不需要的 buffer 来腾出空间,要想粘贴 buffer 的内容可以使用 parse-buffer 命令。

attach/detach/list/kill/rename session

当我们创建了一个 session,就可以在其中遨游了。除了创建 session 外对于 session 还有以下几个操作:

  1. attach-session -t <session-name> -d: 连接到一个 session, -d 指定断开其他会话
  2. <prefix>d: 从一个 session 中分离,所谓的分离就是让 tmux 在后台执行,而连接就是重新连接到后台运行的 session
  3. list-sessions: 列出所有 session

tmux 默认会同步同一个会话的操作到所有会话连接的终端窗口中,这种同步机制,限制了窗口的大小为最小的会话连接。因此当你开一个大窗口去连接会话时,实际的窗口将自动调整为最小的那个会话连接的窗口,终端剩余的空间将填充排列整齐的小圆点,如果存在这种情况我们可以使用 attach-session -d 来断开其他该会话的连接。

分离和连接都不会影响在 session 中执行的任务,如果想要实现关闭当前的 session 有两种方式:

  1. 如果位于 shell 中,执行 exit 退出 shell 的同时关闭了该 session
  2. 可以执行 kill-session 命令来关闭当前 session

如果想要关闭所有的 session,使用 kill-server。 默认 session 以整数序命名,我们可以使用 rename-session 来修改他们。

window

一个 session 中包含多个窗口,每一个时刻只有一个窗口被置入前端(有点类似标签页的概念)。对窗口的操作无法就是增删改查:

  1. new-window -n <window-name> -t <index|target>: 增加就是创建新窗口,参数都是可选的。-t 指定索引他默认是整数序,它决定了在状态栏的排序
  2. kill-window -a -t <index|target>: 删除窗口,默认删除 current window,可以指定 -t 来选择要删除的窗口,-a 会删除 -t 指定外的所有窗口
  3. rename window: window 的修改操作通常只有修改名称,这个是有意义的,我们的工作通常都是在不同 window 中进行的,重命名能够提示我们这个窗口在做什么
  4. select-window -l/-n/-p -t <window-index>: 所谓的查找实际上就是选择激活的窗口,需要注意窗口是根据 window-index 来索引的,其中 -p(previous)、-n(next) 都是基于 window-index 定位的,-l(last) 表示上一个 current window,我们可以使用 -t 来切换到对应 window-index 的窗口

pane

一个窗口可以被划分为多个面板,默认情况下窗口会包含一个占据整个窗口的面板。新建面板非常简单就是分割窗口(当然实际上也可以认为是分割面板)。面板是我们管理 tmux 的最小单元,我们运行的程序也都是在面板上进行的,对面板的操作同样类似增删改查:

  1. split-window/split-pane -h/-v -d: 分割窗口和分割面板是等价的,默认上下分割(即水平分割 -v), 可以指定参数来左右分割(即垂直分割 -h)。默认会自动激活新的 pane 可以指定 -d 来避免这一点
  2. kill-pane -a -t <index|target>: 删除面板,默认删除current window,可以指定-t来选择要删除的窗口,-a会删除-t指定外的所有窗口
  3. select-pane -D/-L/-R/-U -t <pane-index>: 每一个时刻只能有一个active pane,我们可以在 pane 间切换。-D(down)、-L(left)、-R(right)、-U(up)能够在上下左右切换
  4. display-panes: 显示面板编号

pane 还有一系列特殊的操作就是更改尺寸以及移动:

  1. resize-pane -D/-L/-R/-U -Z: 更改 pane 大小,其中-D(down)、-L(left)、-R(right)、-U(up)就是在对应方向上增加和减小。-Z能够让一个 pane 独占整个窗口,再次执行-Z则会返回之前的视图
  2. swap-pane -U/-D -s <source-pane> -t <target-pane>: 移动 pane,其中-D(down)、-U(up)表示根据编号向前或先后移动。-s/-t是需要同时定义的,一个指定源一个指定目标

resize-pane -Z后会在状态栏上显示[window-index]:[window-name][Z]其中的 Z 表示pane zoom状态

choosing sessions,windows,panes and buffer

尽管 window 和 pane 提供了select-window/pane命令来选择对应的视图,但是他们都很不直观,tmux 提供了一个 choose-tree 视图来让我们选择,视图是类似下面的内容:

Bash
(0)   - 1: 6 windows (attached)
(1)   ├─>   0: zsh-: "yangguodongdeMac-mini.local"
(2)   ├─> - 1: [tmux]*Z
(3)      ├─> 0: zsh: "yangguodongdeMac-mini.local"
(4)      └─> 1: zsh*: "yangguodongdeMac-mini.local"
(5)   ├─>   2: test_win: "yangguodongdeMac-mini.local"
(6)   ├─>   8: zsh: "yangguodongdeMac-mini.local"
(7)   ├─> + 10: zsh
(8)   └─>   20: zsh: "yangguodongdeMac-mini.local"
(9)   - 2: 1 windows
(M-a) └─> 0: zsh*: "yangguodongdeMac-mini.local"
  • (0-8): 编号, 按下对应的按键能够跳转并激活对应的组件
  • 1: 6 windows(attached): 代表 session 对应了[session-name]: [window-count] windows[(attached)](attached session)
  • 0: [tmux]*: "yangguodongdeMac-mini.local": 代表 window ,对应了[+](display pane) [window-index]: [window-name]: [hostname] [*](current window)
  • 1: zsh*: "yangguodongdeMac-mini.local": 代表了 pane,对应[pane-index]: [pane-title]: [hostname] [*](active pane)

根据打开的不同层次 tmux 提供了choose-client/session/window他们实际上都是一样的,只不过展开的层次不一样而已。在这个面板下提供了类 vim 的快捷键(左右操作表示打开和关闭折叠),回车可以激活所选的组件,q 能够直接退出 choose 面板。

还有一个特殊的就是choose-buffer可以列出所有的 buffer 的内容,我们可以选着来在active pane中粘贴。

快捷键和命令

快捷键 命令 描述
<prefix>? lsk -N 查看所有按键绑定
<prefix>: - 激活交互式命令行
- new-session -n <session-name> 创建 session
<prefix>d detach 分离 session
- list-sessions 列出所有 session
- attach-session -t <session-name> 连接到一个 session
<prefix>$ rename-session 重命名 session
- kill-session 关闭当前 session
- kill-server 关闭所有 session
<prefix>c new-window -n <window-name> -t <index/target> 创建 window,可以指定名称和索引(非必须)
<prefix>0~9 select-window -t [0-9] 切换到编号为 0-9 的 window
<prefix>' select-window -t <window-index> 打开命令行输入要去切换的window-index
<prefix>n select-window -n 切换到下一个 window
<prefix>p select-window -p 切换到上一个 window
<prefix>l select-window -l 切换到上一个 current window
<prefix>, rename-window 重命名 window
<prefix>& kill-window 删除当前 window
<prefix>% split-pane -h 垂直分割 pane
<prefix>" split-pane -v 水平分割 pane
<prefix>Up/Left/Right/Down select-pane -U/-L/-R/-D 激活上/下/左/右的 pane
<prefix>q[0-9] select-pane -t [0-9] 激活对应编号的 pane
<prefix>q display-panes 显示pane-index
<prefix>x kill-pane 删除当前 pane
<prefix>{ swap-panes -D 与下一个 pane 交换
<prefix>} swap-panes -U 与上一个 pane 交换
<prefix>C-Up/C-Left/C-Right/C-Down resize-pane -U/-L/-R/-D 更改 pane 大小
<prefix>z resize-pane -Z 最大化 pane
<prefix>s choose-session 打开session choose面板
<prefix>w choose-tree/choose-window 打开window choose面板
<prefix>[/] copy mode 开启/关闭 copy mode

配置

tmux 可以使用配置文件进行配置,默认的配置文件为$HOME/.tmux.conf, 在 tmux 3.1 中遵循了 XDG 规范,允许$XDG_CONFIG_HOME/tmux/.tmux.conf作为配置文件。

配置文件语法

tmux 配置文件非常简单,# 开头的为注释,每一行都是一个生效的配置。命令参数有三种表示形式:

  1. 字符串: 特点是空格必须被转义
  2. 使用''包括: 将~\ ~~作为常规字符,不进行转义
  3. 使用""包括: 与字符串唯一的区别就是不需要转义空格
Bash
# 下面四个是等价的
set -g status-left "hello word"
set -g status-left "hello\ word"
set -g status-left 'hello word'
set -g status-left hello\ word
# 注意 '' 中不会转义, 下面是等价的
set -g status-left 'hello\ word'
set -g status-left "hello\\ word"
set -g status-left hello\\\ word
# ~ 作为特殊字符表示家目录,注意在 '' 中就是常规的 ~
# 可以设置环境变量, 他将被 tmux 的 pane 中使用
# 系统环境变量可以直接使用
MYFILE=myfile
source "~/$MYFILE"

按键绑定

使用bind-keyunbind-key命令可以更改 tmux 的按键绑定。每一个键位绑定都属于一个键位表,tmux 默认包含四个键位表:

  1. root: 该键位表下的按键不需要前缀键激活
  2. prefix: 核心的键位表,他们都需要使用前缀键来激活
  3. copy-mode: 只有激活 copy mode 并且是emacs-style下的按键绑定
  4. copy-mode-vi: 只有激活copy mode并且是vi-style下的按键绑定

在进行按键绑定的时候,可以使用-T <key table>来指定要绑定到的键位表,如果不指定默认位于 prefix 中:

Bash
bind-key    -T prefix C-b     send-prefix
bind-key    -T prefix C-o     rotate-window
bind-key -T copy-mode C-a send-keys -X start-of-line

unbind-key用于解绑按键,这并不是必须的,bind-key会覆盖掉他已经绑定的键位。 unbind-key更多的是为了统一风格。

复制模式

copy mode 下有一套独立命令以及键位映射方式:

Bash
# 绑定 copy-mode key-table
# 指定按键 <C-a>
# send-keys -X 可以认为是固定写法
# start-of-line 命令
bind-key -T copy-mode C-a send-keys -X start-of-line
命令 emacs(1) vi(1) 描述
begin-selection C-Space Space 开始选择
cancel q q 退出 copy mode
clear-selection C-g Escape 清空选着
copy-selection-and-cancel M-w Enter 复制所选并退出 copy mode
cursor-down Down j 向下移动光标
cursor-left Left h 向左移动光标
cursor-right Right l 向右移动光标
cursor-up Up k 向上移动光标
end-of-line C-e $ 移动光标到行尾
next-word-end M-f e 移动到下一个单词尾
start-of-line C-a 0 移动光标到行首
page-down PageDown C-f Page down
page-up PageUp C-b Page up
previous-word M-b b 移动到上一个单词

options

tmux 内置了一系列的 options ,他们具有五个影响空间:

  1. server: 影响 tmux-server
  2. session: 影响 session
  3. window: 影响 window
  4. pane: 影响 pane
  5. user: tmux 并不使用但是会为用户保留

选项又分为全局和局部的,例如全局窗口选项会影响所有的窗口,而局部窗口选项只影响一个窗口。可以使用 show-options 来查看所有的选项,其中可以指定-s/-w表示 session window 选项,使用-g表示全局的。

要在配置文件中更改选项的值需要使用set [-g][-u] [option] [option-value]的形式来设置。其中-g表示设置全局选项,-u表示取消设置(也就意味着恢复默认值)。options-value 有两种形式:

  1. 字符串: 如果是需要对应的值
  2. off|on: 如果是开关选项

运行脚本

可以直接在 tmux 配置中使用 run 来运行脚本:

Bash
~/.tmux/zoom 是脚本路径
bind z run ". ~/.tmux/zoom"

个人配置

该配置的起点源于.tmux, 目前就做了键位的绑定。

Bash
# 可以使用 list-keys 来列出所有的键位映射
# 设置 C-b 为 prefix 这实际上是默认按键
set -g prefix C-b
unbind C-b
# 这一句确保可以向其他程序发送 C-b 键位,也就是连续按下两次 C-b
bind C-b send-prefix

# 设置默认的终端模式为 256 色模式
set -g default-terminal "screen-256color"

# 鼠标支持
set -g mouse on

# 窗口操作
## 分割窗口
bind s split-window -h -c "#{pane_current_path}"
bind S split-window -v -c "#{pane_current_path}"
bind r command-prompt -I "#W" { rename-window "%%" }
bind X confirm-before -p "kill-window #P? (y/n)" kill-window
bind C new-window -c "#{pane_current_path}" # 以当前面板路径来创建行窗口

# 面板
## 在面板之间移动
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
## 调整面板大小
### -r 表示可以重复按键,也就是说不需要在按下 prefix 可以连续切换
bind -r < resize-pane -L 5
bind -r + resize-pane -D 5
bind -r - resize-pane -U 5
bind -r > resize-pane -R 5

# copy-mode
## 开启 vi 按键
setw -g mode-keys vi
bind -T copy-mode-vi v send-keys -X begin-selection
bind -T copy-mode-vi y send-keys -X copy-selection-and-cancel
## 打开 buffer 列表
bind b choose-buffer

# esc 响应时间,主要是 vim 中使用
set -s escape-time 0

按键绑定

按键/命令 说明
<C-b> prefix
<prefix>: 打开交互式命令行
tmux n -t <session-name> 创建 session
tmux a -t <session-name> 进入 session
<prefix>d 分离 session
<prefix>c 创建 window
<prefix>C 创建与当前窗口同路径的 window
<prefix>0~9 切换到编号为 0~9 的 window
<prefix>n/p 切换到上一个、下一个 window
<prefix>r 重命名 window
<prefix>X 删除当前 window
<prefix>s 垂直分割 window
<prefix>S 水平分割 window
<prefix>hjkl 上下左右移动 active pane
<prefix>x 删除当前 pane
<prefix><>-+ 调整 pane 大小
<prefix>z 最大化/还原 pane
<prefix>w 打开 session/window/pane 列表
<prefix>b 打开 buffer 列表
<prefix>[ 激活 copy mode
<prefix>] 粘贴
<copy mode>v 开始选择
<copy mode>hjkl 移动光标
<copy mode>y 复制并退出 copy mode
<copy mode>q 直接退出 copy mode

其他特殊设置

中文乱码

当我们在 tmux 中运行 nvim 时会发现不显示中文,则通常是没有设置 locale到 utf-8 导致的:

Bash
# 默认是 C ,在 MacOS/WSL 中是如此
export LANG="zh_CN.UTF-8"
export LC_ALL="zh_CN.UTF-8"

工作目录和 python 虚拟环境

当我们新建窗口或者面板都是进入 tmux 的工作目录(注意不是当前窗口的工作目录)。tmux 默认的工作目录是通过tmux a进入 tmux 时所在的目录。如果我们想要根据当前窗口所在的工作目录来创建窗口或窗格需要指定-c参数:

Bash
bind  c  new-window      -c "#{pane_current_path}"
bind  %  split-window -h -c "#{pane_current_path}"
bind '"' split-window -v -c "#{pane_current_path}"

参考How to create a new window on the current directory?

对于 python 虚拟环境是同样的道理,我们可以在进入 tmux 之前先激活 python 环境,之后进入 tmux 后所有新创建的窗口以及面板都会继承该环境(通常不会有 venv 这样的提示,但是 python 执行路径确实是虚拟环境)。因此我们可以用不同的 session 来管理不同的虚拟环境。

Tmux split window and activate a python virtualenv中可能找到更优雅的方式

vim 颜色突变

这是由于终端类型导致的。shell 有一个环境变量$TERM他表示终端类型,最初的终端都是物理工作台,他的值可能是hp2621这样的具体物理显示器的型号。之后的终端通常都是虚拟终端,所以更多都是linux bsdos这样的形式。

而当前$TERM的经典值是xterm-256color表示支持 256 色的终端。最初的终端都是单色的(显像管),之后发展到 16 色。而目前主流的终端都支持 256 色。tmux 中 vim 的突变就是由于 tmux 中的终端类型默认是 16 色的。我们可以通过echo $TERM来查看。 xterm 算是最为古老的终端模拟器,因此这个名字被延续了下来。

因此要想在 tmux 中启用 256color 需要set -g default-terminal "screen-256color",tmux 中的终端被命名为 screen。

参考