在学习 tmux 之前,我觉得 iTerm2 已经足够用了。
在学会 tmux 之后,我完全彻底地改变了这种看法。
注:这篇文章很长,并且在更新中。我希望你能耐心地读完,并且一次性学会 Tmux 的日常使用。有任何问题,请评论。
Terminal 与 iTerm2
在介绍 “后浪” tmux 之前,先介绍一下它的 “前辈” 们。
Mac OS 自带的终端模拟器 Terminal(终端),只能一个窗口打开一个终端。要想打开多个窗口,需要运行多个 Terminal。窗口之间相互独立。这使得在窗口之间切换时并不方便。用过 IE 6 吧?
后来出现了 iTerm2,相比于 Terminal 来说,iTerm2 支持了标签页(TAB)。Terminal 和 iTerm2 在这方面的比较,就像是 IE 6 与 Chrome 的区别。 除此之外,iTerm2 还支持一个窗口(Window)拆分成多个窗格(Pane)。这比 Terminal 好用了很多。这些都是刚需。
不论是 Terminal 的窗口多开、还是 iTerm2 的多个标签页或窗格拆分,它们新产生的终端都是利用的是你正在使用的操作系统的 GUI 图形功能实现的,即打开的是另一个几乎完全独立的一个终端。 比如:一个窗口 ssh 到了服务器,如果此时新开一个窗口(或标签或窗格),你需要再次 ssh,才能到达服务器。
所以, tmux 有何不同?
Tmux 介绍
Tmux (terminal multiplexer) 是一款终端多路复用(Multiplexer)软件。
与前面提到的几款终端模拟器最大的不同之处在于:
它是一款命令行软件,它的多窗口、多窗格模式是在 CUI 模式下实现的。
这意味着,你可以只打开一个终端,然后在里面不停地用 tmux 打开新的窗口、窗格等。而如果你的 tmux 是 ssh 到服务器之后在服务器上运行的,你将拥有在服务器在打开多个窗口、多个窗格的能力。 这样一来,你就不必多次 ssh 到同一台服务器上,只为执行不同的命令了。而且还不用担心断网,因为可以重连。
Tmux 还拥有非常强大的命令及快捷键系统,跟 Vim 类似。
是不是很优秀? 想不想学?
几个概念
在继续介绍之前,首先介绍 tmux 的几个名词。从小到大依次介绍:
-
窗格(Pane)
一个窗格即一个真正的独立的终端界面,它起始于用户的 shell。就像是你新打开了一个 Terminal。
-
窗口(Window)
一个窗口包含多个窗格,也可以说是一个窗口被拆分成了多个窗格。 记得 Windows 操作系统的图标吗?1 个窗口被分成了 4 个窗格。 就像是你在 iTerm2 内把一个窗口拆分成多个窗格一样。
一个窗口(内的所有窗格)将占据整个屏幕。
也可以按最大化快捷键,将当前活跃的窗格最大化,此时该窗格将占据整个窗口。
-
会话(Session)
一个会话包含多个窗口。由于一个窗口即占据整个屏幕,所以需要按快捷键在不同的窗口之间切换才能显示其它的窗口。
客户端每次只连接到一个会话,但是可以在会话之间切换。
-
服务器/标签(Label)
这里的标签的含义是给 tmux 服务器取一个名字。一个标签即一个服务器。
一个服务器管理多个会话。
默认运行时,标签名字叫“default”,路径为:
/tmp/tmux-<uid>/default
。其实很少有启动不同 tmux 服务器的需要,我们通常做的是创建不同的会话、不同的窗口、不同的窗格。测试是一个例外。
-
用户(User)
不同的系统登录用户之间运行的 tmux 服务器当然应是隔离的。文件系统路径:
/tmp/tmux-<uid>/
。
概念似乎挺多,但其实非常好理解。我们反过来再理解一下。
- 一个系统可以登录多个用户
- 一个用户可以启动多个标签
- 一个标签可以包含多个会话
- 一个会话可以打开多个窗口
- 一个窗口可以拆成多个窗格
是不是很好理解了?
当然,一个窗格就是最原始的 shell。你可以在里面再次运行 tmux,天呐…… Tmux 默认不允许这样做 :-)
非常实用的特性
Tmux 有哪些与众不同、出类拔萃的功能?
-
多窗口、多窗格功能在终端内实现
只需要打开一个终端模拟器窗口或一个 ssh 会话,即可实现多窗口、多窗格。
-
从会话分离、重新连接会话
从会话分离,意味着回到原始的 shell 提示符。重新连接到会话,意味着从命令提示符重新进入 tmux 实现的多窗口、多窗格界面。 这种行为就像你在最小化和还原一个图形窗口一样。
所以你不必再把你要运行的命令放到后台执行了,你只需要把 tmux 最小化即可(分离会话)。
这个能力的实现,得益于 tmux 的 客户端/服务端(C/S) 模型。
-
不受 网络连接/ssh 断开的影响
当直接在 ssh 后的 shell 里面执行命令时,如果此时 ssh 连接不小心断开,你就再也无法回到连接断开前的界面了。正在执行的命令可能也会立即退出。 而 tmux 不一样。断开连接其实就是上述“从会话分离”。ssh 断开后,重新 ssh 到服务器,再“重新连接会话”即可回到连接断开前的 tmux。
这一点超级实用!再也不用担心网络连接突然异常断开了。
-
多个客户端可以连接到一个会话
多个客户端连接到同一个会话,可以让它们显示完全一样的内容。但是客户端的显示区域大小不一样怎么办?会以最小的客户端大小重新调整大小。
-
方便地在窗口、会话之间切换
-
非常强大的命令及快捷键系统
Tmux 的各种能力通过命令来完成,而且绝大多数命令可以绑定到快捷键来完成复杂的工作。
关于 Tmux 的命令及快捷键,将会在后面独立的章节介绍。
Tmux 的实用特性远不止这么多,还需要耐心挖掘。
状态栏
状态栏默认位于屏幕底部,用于显示:当前会话名,当前会话的窗口列表,当前的主机名及时间(可自定义)。
注意,状态栏是 tmux 的一个非常明显的标识。绿色的一长条,位于屏幕底部。
一个正在运行的 tmux 截图
注:在移动设备下,可能显示错乱。
version: "2" │1 package main
services: │ 1
nginx: │ 2 // Path ...
image: nginx:1.17.8 │ 3 type Path struct {
container_name: nginx │ 4 > Name string `yaml:"name"`
network_mode: host │ 5 > Token string `yaml:"token"`
volumes: │ 6 > Local string `yaml:"local"`
- ./conf:/etc/nginx:ro │ 7 > Remote string `yaml:"remote"`
- ./html:/usr/share/nginx/html:ro │ 8 }
- ./logs:/var/log/nginx │ 9
- ..:/etc/nginx/servers:ro │ 10 // Config ...
restart: unless-stopped │ 11 type Config struct {
│ 12 > Listen string `yaml:"listen"`
root@twofei:~/servers/nginx# │<tcp/config.go" 18L, 358C 1,1 Top
─────────────────────────────────────────────┼────────────────────────────────────────────
Active Internet connections (only servers) │Filesystem Size Used Avail Use% Mounte
Proto Recv-Q Send-Q Local Address F│udev 345M 0 345M 0% /dev
tcp 0 0 127.0.0.1:2563 0│tmpfs 74M 7.5M 66M 11% /run
tcp 0 0 127.0.0.1:2564 0│/dev/vda1 34G 8.7G 24G 28% /
tcp 0 0 127.0.0.1:3306 0│tmpfs 367M 0 367M 0% /dev/s
tcp 0 0 0.0.0.0:80 0│tmpfs 5.0M 0 5.0M 0% /run/l
tcp 0 0 127.0.0.1:3826 0│tmpfs 367M 0 367M 0% /sys/f
tcp 0 0 127.0.0.1:8021 0│tmpfs 74M 0 74M 0% /run/u
tcp 0 0 0.0.0.0:22 0│overlay 34G 8.7G 24G 28% /var/l
tcp 0 0 0.0.0.0:443 0│shm 64M 0 64M 0% /var/l
tcp 0 0 127.0.0.1:40479 0│overlay 34G 8.7G 24G 28% /var/l
tcp6 0 0 :::22 :│shm 64M 0 64M 0% /var/l
tcp6 0 0 :::8443 :│overlay 34G 8.7G 24G 28% /var/l
: │:
[twofei] 1:proxy 2:mysql 3:blog 4:nginx 5:taolang 6:play* 7:http2tcp- 01:20
Tmux 的命令系统及基本使用
Tmux 毕竟是一款命令行软件,所以用命令来完成各种任务再适合不过了。
基本语法:
1
|
|
下面介绍的基于命令的用法只是让你能快速掌握 tmux 的基本用法。命令式的操作比起快捷键来说要麻烦得多,但是鉴于我们还没学习快捷键,所以暂且如此。
新建会话
在 shell 内执行 tmux
即可。此时没有提供 [命令]
和 [选项]
,默认为 tmux new-session
,即创建新的会话。
在执行此命令时,若 tmux 发现当前用户没有启动服务器,则会创建一个名为“default”的服务器,然后才创建一个会话。
新的会话创建好之后,同时会创建一个新的窗口,这个窗口包含一个窗格。
大多数时候我们只关心“会话”、“窗口”、及“窗格”。我们很少创建多个服务器。
示例图(没有展示颜色):
注:我的命令提示符是当前目录后面跟一个向右的箭头。并且我的 tmux 是配置过的,如果你还没有创建过 tmux 配置文件,显示可能稍有区别。
~ →
[1] 1:bash* 17:14
提示:new
命令是 new-session
的别名,可以代替使用。
拆分窗口为窗格
拆分可以水平拆,也可以竖直拆。
- 竖直拆分:
tmux split-window
- 水平拆分:
tmux split-window -h
以下是我一次竖直拆分、一次水平拆分后的结果:
~ → tmux split-window
~ →
────────────────────────────────────────┬───────────────────────────────────────
~ → tmux split-window -h │~ →
~ → │
│
│
│
│
│
│
│
│
│
[6] 1:bash* 17:22
可以看到,一个窗口首先因为竖直拆分被拆分成了上下两个窗格,然后由于水平拆分,下面的窗格再次被拆分成了两个窗格。
在窗格之间切换
每次只有一个窗格处理“选中(活跃)”状态,所以需要切换使用的窗格。
默认设置下,Tmux 是未打开鼠标支持的,并且我们还没有讲解到如何用快捷键切换,所以暂时用命令来完成吧。
- 选择左边的窗格:
tmux select-pane -L
,L 即 Left; - 选择右边的窗格:
tmux select-pane -R
,R 即 Right; - 选择上边的窗格:
tmux select-pane -U
,U 即 Up; - 选择下边的窗格:
tmux select-pane -D
,D 即 Down;
当前活跃的窗格的边界会以绿色显示出来,注意观察。
窗格最大化与还原
命令:tmux resize-pane -Z
第一次执行时,当前窗格将占据整个窗口(即最大化)。再次执行后,将还原。
此时状态栏的当前窗口名(带“*”的)后面将会显示一个“Z”字符,表示 Zoomed,即最大化了。
这在窗格太多,显示区域太小的时候非常有用。
将窗格独立到新窗口
命令:tmux break-pane
除了上述的最大化窗格,还可以用这个把窗格独立到新窗口。
新建窗口
窗格拆得太多会很越来越小,此时可以新建窗口以完成其它的任务。
命令:tmux new-window
~ →
[6] 1:bash- 2:bash* 17:38
窗口列表在底部状态栏显示着,当前的窗口名后面带一个*
号。上一次的窗口名后面带一个-
号。
这个窗口跟最开始的窗口无异。拆分窗格你随意进行就好。
切换窗口
在一个窗口内切换窗格刚刚已经学会了,现在来学如何切换窗口。
命令:tmux select-window -t :=<窗口编号>
,看起来有点长。
每个窗口都有一个数字编号,位于底部状态栏窗口列表中每个窗口名的前面。
比如,要选择窗口 2:tmux select-window -t :=2
。
Tmux 最小化与还原
Tmux 最小化即前面提到的客户端:“会话分离”、“会话重连”。
- 会话分离:
tmux detach-client
(相当于最小化) - 会话重连:
tmux attach-session
(相当于还原)
在最小化之后,你就可以回到最初的 shell 命令行了。这有点类似于按 C-z
把 vim 最小化,然后 fg
将其还原。
不管 tmux 有没有最小化,你都可以非常安全地断开 ssh 连接,或者关闭终端模拟器。然后在重新打开终端或ssh后重连会话回到 tmux。这在前面已经介绍过。
注意:client 与 session 可以简单看作是同一个概念,因为一个 client 连接到一个 session。不必过分纠结。
提示:a
是 attach-session
的别名,可以代替使用。
切换会话
切换会话之前,你应该知道当前有哪些会话列表:
当前会话列表:tmux list-sessions
示例输出:
1 2 3 4 |
|
行首的数字是会话的名字,因为是自动生成的,所以看起来没啥辨识度,后面会讲到更名。
然后记得前面的数字,执行以下命令切换过去:
切换到会话:tmux switch-client -t <会话名>
这个过程看起来挺繁琐的,事实上的确如此。所以,有一个更好的交互式命令用来快速切换会话:
交互式切换会话:tmux choose-tree -sZ
,s 表示 session,Z 表示 zoomed。
下面是示例图:
(0) + 0: 2 windows (attached)
(1) + 6: 2 windows
(2) + 7: 1 windows
┌ 0 (sort: index)──────────────────────────────────────────────────────────────┐
│ t interactive shell is now zsh. │2020/05/16 05:11:33 accept: 127.0.0.1: │
│ your account to use zsh, please run `│2020/05/16 05:14:19 accept: 127.0.0.1: │
│ etails, please visit https://support.│2020/05/16 17:02:08 accept: 127.0.0.1: │
│ plit-window │2020/05/16 17:05:02 accept: 127.0.0.1: │
│ ┌───────┐ │2020/05/16 17┌──────────┐t: 127.0.0.1: │
│ │ 1:vim │ │2020/05/16 21│ 2:[tmux] │t: 127.0.0.1: │
│ └───────┘ │2020/05/17 04└──────────┘t: 127.0.0.1: │
│ │2020/05/17 04:57:38 accept: 127.0.0.1: │
│ │2020/05/17 17:58:03 accept: 127.0.0.1: │
│ │ │
└──────────────────────────────────────────────────────────────────────────────┘
[0] 1:vim- 2:[tmux]* 18:08
窗口上半部分展示了一颗“树”:首先是会话列表,然后展开后是会话内包含的窗口列表,再然后是窗口内包含的窗格列表。
窗口下半部分是会话、窗口、窗格的预览图。
你可以使用方向键(如果你会 vim,可以用 hjkl)上下切换或开展与折叠,导航到你想要去的会话、窗口或窗格时,按下回车即可切换过去。
给会话及窗口命名
默认的会话是以数字递增命名的,基本没有辨识度。所以我们应当重命名它。
命名会话:tmux rename-session <会话名>
以下是示例,注意左下角的变化:
~ → tmux rename-session 这是一个会话名
~ →
[这是一个会话名] 1:bash* 2:bash- 18:17
当然,切换会话的时候就有辨识度了:
(0) + 0: 2 windows
(1) + 7: 1 windows
(2) + 这是一个会话名: 2 windows (attached)
[这是一个会话名] 1:[tmux]*Z 2:bash- 18:18
重命名窗口也很类似:
命名窗口:tmux rename-window <窗口名>
~ → tmux rename-window 窗口名
~ →
[这是一个会话名] 1:窗口名* 2:bash- 18:20
注意到底部的窗口名的变化。
切换会话时:
(0) + 0: 2 windows
(1) + 7: 1 windows
(2) - 这是一个会话名: 2 windows (attached)
(3) ├─> - 1: 窗口名*Z (3 panes)
(4) │ ├─> 0: bash "MacBookPro-Home.local"
(5) │ ├─> 1: bash "MacBookPro-Home.local"
(6) │ └─> 2: bash "MacBookPro-Home.local"
(7) └─> 2: bash- (1 panes) "MacBookPro-Home.local"
[这是一个会话名] 1:窗口名*Z 2:bash- 18:21
快捷键系统
你会 Vim 或 Emacs 吗?这两者都有着非常强大的快捷键系统。
Vim 被很多人用来修改配置文件,熟悉的人挺多。Emacs 呢?正如我在 Bash 终端常用快捷键总结 提到的那样,你在编辑命令时,那些常用的 C-a
、C-e
其实就是 Emacs 的快捷键呢。
Tmux 也有着与它们类似的复杂的快捷键系统。老实说,短时间内要记住它们挺难的,还需要多操练。有没有被吓到?
Vim 有 Leader 键,Tmux 有 Prefix 键。Prefix 键拿来干啥?
因为默认情况下,你在窗格内进行的应该是命令编辑操作,就像你在普通的终端那里敲字符编辑并执行命令一样。 所以为了不让普通的字符有特殊的功能,应该在按下字符前加一个 Prefix,即前缀。
Tmux 默认的 Prefix 键是 C-b
,即 Ctrl-b
。
按下 Prefix 之后,就可以再输入一个字符即可完成快捷键的录入。
多数常用的命令都会以 Prefix 开始。
比如:C-b c
,即先按下 Ctrl-b
,再按下 c
。可以创建一个新的窗口。
默认的 Prefix 快捷键列表
以下是部分常用的默认 Prefix 快捷键列表(记得先按 Prefix 键):
? 显示所有的快捷键(不止是 Prefix 表中的,后面会介绍)
c 新建窗口
" 竖直拆分当前窗格为上下两个
% 水平拆分当前窗格为左右两个
! 把当前窗格拆分到新窗口
z 最大化窗格与还原
$ 重命名当前会话
, 重命名当前窗口
s 交互式选择会话、窗口、窗格
d 分离会话
0 to 9 选择编号为 0-9 之一的窗口
= 浏览剪贴板列表,回车粘贴
: 进入命令末行模式(似 vim),此时可以输入命令,不用带最前面的 tmux
r 重绘 tmux 窗口(有时候不小心清屏了可以用这个显示出来)
t 显示当前时间
] 进入拷贝模式
方向键 切换窗格
如何发送 Prefix 键给原来的进程?
C-b
在 shell 里面默认是光标回退一个字符(详见:Bash 终端常用快捷键总结),但是被 tmux 截获并作为了 Prefix 键。C-b
不能再提供原来的功能了。
怎么办? 很简单,按两次即可,即:C-b C-b
。默认情况下这个快捷键就提供了。
进入末行命令模式
有时候正在运行一个全屏的程序,要回到 shell 并执行一个 tmux 命令
并不友好。所以,末行模式可以暂时用来完成命令的执行。
进入末行命令模式(类似 vim):Prefix
+ :
,你应该知道怎么输入冒号吧?冒号在分号的上面,需要按 Shift + ;
输入。
进入后,底部状态栏会有一个命令提示符,你就可以输入并执行命令了。由于是在 tmux 环境内执行的,你不用再输入命令开始的 tmux
。
快捷键的分表
Tmux 的快捷键分布在不同的“表”中,前面提到的 Prefix 表是其一。在 Prefix 表中的快捷键,需要先按下 Prefix。
除 Prefix 表以外,还有 root 表,copy-mode 表等。在 root 表中的快捷键不需要像在 Prefix 表中的快捷键那样需要先按一个前缀,而是直接按下即可。
比如,你可以自定义绑定c
为创建新窗口,然后,简单地按下c
就可以创建新窗口了,而不需要再先按下像是 Prefix 那样的组合键。
但是,正如前面所说,默认情况下,你应该是在编辑将要被执行的命令,而不是按下快捷键,所以,root 表用得并不是特别多。绑定在 root 表中的快捷键也并不多。
当然,也并不是没有用。后面讲解“鼠标支持”的时候会看到实用功能。
copy-mode 表是什么表?终端通常会有很多输出(来自 stdout 和 stderr),特别是当有大量输出时,非常不利于我们复制终端内的内容。 所以,正如其名字所代表的,这个表是 copy-mode(拷贝模式)下所要用到的表。所以,什么是“拷贝模式”?后面再详述。
快捷键的绑定与取消
默认快捷键其实挺好的,但毕竟众口难调,操作习惯也不一样。所以,自定义快捷键的绑定与取消是很实用的。
快捷键独立于表的,所以要绑定快捷键,就要事先说明绑定在哪个表中。
- 绑定快捷键语法:
bind -T <表名> <按键> <命令>
- 解绑快捷键语法:
unbind -T <表名> <按键>
注意:我没有再在前面加 tmux
前缀了,你自己应该知道什么时候应该加了吧?<命令>
处也不用加tmux
。
提示:
- 不指定表名时,默认绑定到 Prefix 表
bind -n
等价于bind -T root
(n 即 normal)bind -r
表示这个快捷键可以重复执行(r 即 repeatable)
示例:
# 绑定按 c 直接创建窗口
bind -n c new-window
# 解绑按 c 直接创建窗口
unbind -n c
# 绑定按 Prefix Esc 为隐藏状态栏
bind Escape set status
对于我等 vim 党不喜欢方向键的人来说,用 h/j/k/l 来代替 左/下/上/右 是太正常的事了:
bind -r h select-pane -L
bind -r j select-pane -D
bind -r k select-pane -U
bind -r l select-pane -R
听说 HHKB 机械键盘 没有 方向键?Oh my god!我是一定不会用的,一点都不香。(其实主要是穷)
我本人并不建议使用 tmux 的初期就修改 Prefix 键(马上说)和绑定太多键。因为:对于像我这种经常登录到公司的各种服务器上的人来说,我可没精力把配置文件全部同步到每台服务器上。 所以,熟悉默认按键总是好的,不会一时忘了套。
另,我应该会写一篇新的文章来贴出我的 tmux 配置(我觉得真的挺好用的)。如果没有,就当我没说,你也没看到。
自定义 Prefix 键
打脸,刚刚说了不应该一开始就修改按键绑定,立马就修改了。。
正如前面所提及的,C-b
在 shell 里面是使光标向左移动一个字符的(我可不喜欢方向键,手的移动跨度太大了),我用得很频繁。所以需要修改一下默认的 Prefix 键。
改成什么键好呢?我参考了我几个同事以及网友的:
C-a
,这跟C-b
没差啊,C-a
是回到命令行的行首。大量使用的快捷键为什么要被替换掉;C-o
,这是 vim 的在 Insert 模式下临时切换到 Normal 模式的快捷键啊,你怎么老是跟我作对?;
后来我选了 C-j
,C-j
在 shell 里面相当于按下回车,好像没啥用处。那就这样吧。
注意:修改 Prefix 是通过 set
命令来完成的。
# 默认的 Prefix 键
set -g prefix C-j
# 两次按下 Prefix 发送 Prefix 给原来的进程
bind C-j send-prefix
# 取消原来的 Prefix 绑定
unbind C-b
鼠标支持
在没有开始鼠标支持时,不能用鼠标来滚动历史内容、不能用鼠标来切换窗格、不能用鼠标来改变窗格的大小。挺不方便的。 并且,滚动鼠标滚轮居然默认是滚动历史命令,而不是历史内容,谁能忍???
新版本(不是太老即可)的 tmux 已经能一键开启鼠标支持了。命令如下:
set -g mouse on
拷贝模式
在拷贝模式下,屏幕会暂停更新,并且有一张新的快捷键表。这时候可以通过鼠标来选择文字进行复制。
拷贝模式的快捷键默认是 Emacs 风格的,不熟悉,改成 vim 应该会熟悉很多。
# 拷贝模式使用 vim 风格的快捷键
set -wg mode-keys vi
# 解绑原来的所有 emacs 快捷键
unbind -a -T copy-mode
进入拷贝模式的方式:Prefix ]
如果开启了鼠标支持,则回滚历史、鼠标选择文本默认都会自动进入拷贝模式。
在 vim 拷贝模式下,大部分 vim 类似的滚屏,光标移动都是可以使用的。 另外有一些特殊的:
Space
进入文字选择模式Escape
清除选中文字
暂时先写这么多吧,几个小时了,好饿。。。