Linux运维-Tmux使用技巧与最佳实践

Tmux简介

TmuxTerminal Multiplexer 的简称,它是一款优秀的终端复用软件,类似 GNU screen,但比screen更出色。tmux来自于OpenBSD,采用BSD授权。使用它最直观的好处就是, 通过一个终端登录远程主机并运行tmux后,在其中可以开启多个控制台而无需再“浪费”多余的终端来连接这台远程主机, 还有一个好处就是当终端关闭后该shell里面运行的任务进程也会随之中断,通过使用tmux就能很容易的解决这个问题。

Tmux和screen的比较

TmuxScreen 都是用于在 Unix-like 系统中进行终端多路复用的工具,它们具有类似的功能,但也有一些区别。下面是它们的优点和区别:

Tmux 的优点和特点:

  • 更现代的代码:tmux 是一个相对较新的项目,代码更现代化、维护更活跃,具有更好的可扩展性和可定制性。
  • 更丰富的功能:tmux 提供了更丰富的功能集,包括窗格(窗格s)、窗口(windows)、会话(sessions)等,可以更灵活地管理多个终端会话。
  • 更好的自定义性:tmux 支持通过配置文件进行各种自定义设置,用户可以根据自己的喜好和需求来配置键绑定、外观等。
  • 更强大的脚本支持:tmux 提供了丰富的命令行接口和 API,可以通过脚本来实现更复杂的操作和自动化任务。

Screen 的优点和特点:

  • 成熟稳定:screen 是一个非常成熟且稳定的项目,已经存在了很长时间,并被广泛应用于各种 Unix-like 系统中。
  • 易于上手:screen 的基本用法相对简单,易于上手,适合新手用户快速上手使用。
  • 广泛支持:由于 screen 的历史悠久,它被广泛支持和集成到了许多 Linux 发行版和 Unix-like 系统中,几乎可以在任何系统中找到。
  • 兼容性好:由于 screen 的普及程度和长期存在,它的配置文件格式和用法在不同系统之间基本保持一致,具有很好的兼容性。

区别:

  • 配置和定制:tmux 提供了更灵活和丰富的配置选项,用户可以更精细地定制各种设置,而 screen 则相对简单直接,定制性不如 tmux。
  • 键绑定:tmux 和 screen 的键绑定有所不同,tmux 的键绑定更符合现代习惯,而 screen 的键绑定则相对较老式。
  • 性能:一般认为 tmux 在性能方面略优于 screen,尤其是在大规模或者复杂任务下,tmux 的性能更好一些。

总的来说,tmux 和 screen 都是非常强大的工具,选择使用哪个取决于个人偏好、需求和对功能的要求。新用户可能更倾向于使用 screen,而对于需要更丰富功能和定制性的用户,则可能更倾向于选择 tmux。

Tmux安装使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# tmux官网下载地址:http://tmux.github.io/ 
# Ubuntu版本下可以直接使用apt安装
sudo apt-get install tmux

# CentOS版本下使用yum安装
yum install -y tmux

# 在macOS中安装
# 安装 Homebrew
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
# 安装tmux
brew install tmux

# 查看版本
tmux -V

Tmux的快捷键

tmux操作命令解释
Ctrl+Z挂起当前会话;
Ctrl+B激活控制台;此时以下按键操作生效;
系统操作?列出所有快捷健;按q返回
d脱离兰前会话;这样可以暂时返回Shell界直,输入tmux attach能然重新进入之前的会话
D选择要脱离的会话;在同时开启了多个会话时使用
r强制重绘未脱离的会话
s选择并切换会话;在同时开启了多个会话时使用
:进入命令行模式;此时可以输入支持的命令,例如kill-server可以关闭服务器
[进入复制模式;此时的操作与vi/emacs相同,按q/Esc退出
~列出提示信息缓存;其u包含了之前tmux返回的各种提示信息
窗口操作c创建新窗口;
&关闭当前窗口;
数字1-9切换至指定窗口
p切换至上一个窗口
n切换至下一个窗口
l在前后创建间进行互相切换
w通过窗口列表切换窗口
f在所有窗口中找指定文本
,重命名当前窗口;
.修改当前窗口编号;相当于窗口重新排序;
面板操作"将当前面板平分成上下俩块;
%将当面板平分成左右两块
x关闭当前面板
!将当前面板置于新窗口;即新建一个窗口;其中包含当前面板
Space(空格)在预置面板布局中循环切换;依次包括even-horizontal、even-vertical、main-horizontal、main-vertical、titled
q显示面板编号
o在当前窗口中选择下一个面板
方向键移动光标以选择面板
{向前置换当前面板
}先后置换当前面板
Alt+o逆时针旋转当前窗口的面板
Ctrl+o顺时针旋转当前窗口的面板
Ctrl+方向键以1个单元格为单位移动边缘以调整当前面板大小
Alt+方向键以5个单元格为单位移动边缘以调整当前面板大小

tmux的会话操作

新建会话

启动tmux 窗口第一个编号是0,第二个窗口编号是1,以此类推。这些窗口对应的会话,就是 0 号会话、1 号会话。

使用编号区分会话,不太直观,更好的方法是为会话起名。

1
2
# 新建一个指定名称的会话
tmux new -s wxs1

new_session

分离会话

在 Tmux 窗口中,按下Ctrl+b d或者输入tmux detach命令,就会将当前会话与窗口分离。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 在tmux会话中执行
# 先按Ctrl+b之后再按d或者输入tmux detach命令
# 执行以上命令后,就会退出当前 Tmux 窗口,但是会话和里面的进程仍然在后台运行


#查看当前所有的 Tmux 会话
# tmux ls
# tmux list-session

$ tmux ls
wxs1: 1 windows (created Thu Jul 25 14:16:48 2024) [127x24]

$ tmux list-session
wxs1: 1 windows (created Thu Jul 25 14:16:48 2024) [127x24]
# 注意:
wxs1: 表示tmux会话的名字
1 windows: 表示会话中有1个窗口

tmux-ls

重新进入会话

如果在终端环境中运行 tmux ls 查看有tmux会话正在后台运行,如何进入到该正在后台中运行的会话呢,通过运行 tmux attach -t wxs1 即可进入到该已存在的会话 wxs1 中。其中attach也可以简写成a来表示, -t 指定要进入已存在的会话名,如果不存在则会报告 session not found 错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 重新接入已存在的 tmux 会话
# tmux attach -t <会话编号>
# tmux attach -t <会话名字>
# 注意 -t 后参数输入 tmux list-session 查询结果中 冒号前的字段即可。
若 会话有名字:wxs1 , -t wxs1
若 会话没有名字,已默认编号为名字, -t 默认编号即可

# 会话名是默认编号
$ tmux list-session
0: 1 windows (created Thu Jul 25 14:16:48 2024) [108x19]
1: 1 windows (created Wed Nov 22 17:07:33 2023) [108x19]
$ tmux attach -t 1 # 成功接入 已存在会话,会话名为:1
[detached]

$ tmux ls
wxs1: 1 windows (created Thu Jul 25 14:16:48 2024) [108x19]
$ tmux list-session
wxs1: 1 windows (created Thu Jul 25 14:16:48 2024) [108x19]

$ tmux attach -t wxs1 # new01 : tmux 会话session 名字

杀死会话

我们可以在终端环境和会话环境中销毁会话,例如在终端环境中运行 tmux kill-session -t wxs1 结束名字为wxs1的tmux会话。

在会话环境中运行 ctrl+b : (注意按组合键之后再按一个冒号键),状态栏变成黄色之后提示我们可以在会话环境中输入命令,此时输入 kill-session -t wxs1` 回车即可。其中wxs1是要销毁的会话名。

会话销毁之后,在终端环境中运行tmux ls 或者在会话环境中运行 ctrl+b s 则被销毁的会话不会再出现在会话列表中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1. 使用会话编号
$ tmux kill-session -t <会话编号:即 tmux list-session 查询结果中 冒号前的字段即可>

$ tmux list-session
0: 1 windows (created Thu Jul 25 14:16:48 2024) [108x19]
1: 1 windows (created Thu Jul 22 14:07:33 2023) [108x19]

$ tmux kill-session -t 1

$ tmux list-session
0: 1 windows (created Thu Jul 25 14:16:48 2024) [108x19]

2. 使用会话名称
$ tmux kill-session -t <session-name> # 根据会话名字杀死会话

$ tmux list-session
nws01: 2 windows (created Thu Jul 25 14:16:48 2024) [108x19]

$ tmux kill-session -t nws01

$ tmux list-session # 查询会话
failed to connect to server

切换会话

上述切换会话每次都要退出当前会话,先回到shell终端环境再运行tmux ls 来查看就很不方便,那么在tmux的会话环境中,我们可以通过 ctrl+b s 来获取当前linux机器上tmux所有的后台会话列表,此时可以通过方向键选择会话并回车,在会话间进行切换。

切换会话

重命名会话

我们可以在终端环境中将会话重命名,如上面的命令,重命名之后通过 tmux ls 命令在终端环境中看到的列表中会显示会话的新名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#tmux rename-session -t <old_session_name or default_session_no> <new_session_name>
1. 会话没有名字,只有默认编号
$ tmux list-session # 查询已存在会话
0: 1 windows (created Thu Jul 25 14:16:48 2024) [108x19]
2: 1 windows (created Wed Nov 22 17:16:51 2023) [108x19]

$ tmux rename-session -t 2 nws12 # 将默认编号2的会话重命名 news12

$ tmux list-session
0: 1 windows (created Thu Jul 25 14:16:48 2024) [108x19]
nws12: 1 windows (created Wed Nov 22 17:16:51 2023) [108x19] # 查询编号2的会话名字已更新

2. 回话有名字
$ tmux list-session # 查询已存在会话 1,2
nws01: 1 windows (created Wed Nov 22 16:53:20 2023) [108x19]
nws02: 1 windows (created Wed Nov 22 16:53:27 2023) [108x19]

$ tmux rename-session -t nws01 nws11 # 将会话nws01 重命名为 new11
$ tmux list-session # 查询已存在会话 1,2
nws02: 1 windows (created Wed Nov 22 16:53:27 2023) [108x19]
nws11: 1 windows (created Wed Nov 22 16:53:20 2023) [108x19] # 会话1 名字已更新

tmux的窗格操作

创建分屏

tmux的一个窗口可以被分成多个pane(窗格),可以做出分屏的效果。

1
# ctrl+b " 水平分屏(组合键之后按一个双引号),用一条水平线把当前窗口分成上下两屏。

horizontal

1
# ctrl+b % 垂直分屏(组合键之后按一个百分号),用一条垂线把当前窗口分成左右两屏。

vertical

光标不同窗格切换

1
2
3
4
ctrl+b o 依次切换当前窗口下的各个窗格。
ctrl+b Up|Down|Left|Right 根据按箭方向选择切换到某个窗格。
ctrl+b Space (空格键) 对当前窗口下的所有窗格重新排列布局,每按一次,换一种样式。
ctrl+b z 最大化当前窗格。再按一次后恢复

切换

显示窗格编号

1
2
# 显示窗格编号
Ctrl + b q

编号显示

关闭窗格

ctrl+b x 关闭当前使用中的窗格,操作之后会给出是否关闭的提示,按y确认即关闭。

关闭窗格

tmux中的历史输出查看

在tmux里面,因为每个窗口(tmux window)的历史内容已经被tmux接管了,当我们在每个tmux的window之间进行来回切换,来回操作,那么我们没有办法看到一个window里面屏幕上的历史输出。没办法使用鼠标滚动(例如在SecureCRT中)查看之前的内容,在SecureCRT中通过鼠标滚动看到的输出一定是各个tmux的window的输出混乱夹杂在一起的,如果要看当前窗口的历史内容,那么应该怎么办呢,通过在当前的tmux window 按 ctrl-b 进入copy mode,然后就可以用PgUp/PgDn来浏览历史输出了,按q退出。

tmux的远程批量操作的shell脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
在使用SSH或telnet远程连接服务器时,可能都遇到为一些长时间运行的任务而头疼,比如系统备份、ftp 传输等等。通常情况下我们都是为每一个这样的
任务开一个远程终端窗口,因为他们执行的时间太长了。必须等待它执行完毕,在此期间可不能关掉窗口或者断开连接,否则这个任务就会被杀掉,一切半
途而废了。也许nohup命令可以达到这个目的,如果程序的标准输出/标准错误是终端, nohup默认将其重定向到nohup.out文件。值得注意的是nohup命令
只是使得程序忽略SIGHUP信号,还需要使用标记&把它放在后台运行。

nohup <command> [argument…] &

虽然nohup很容易使用,但还是比较“简陋”的,对于简单的命令能够应付过来,对于复杂交互的程序就麻烦了。

使用tmux打开一个终端窗口,可以在窗口里执行一个长时间运行的交互式命令操作,令其一直在后台跑着,并且在按键ctrl-b-d后,可以无感知的退出窗口,
而退出后窗口不会关闭,即窗口里执行的交互命令也不会结束。这比起传统的"nohup commang & (然后按ctrl+c)"的方式要还用很多。

如何在linux终端里创建一个tmux窗口的同时直接在窗口终端里执行命令呢?
这就需要用到tmux send -t session_name "command" ENTER

示例一
[root@boysec ~]# tmux new -d -s kevin_session && tmux send -t kevin_session '/usr/local/bin/main' ENTER && tmux attach -t kevin_session
[detached (from session kevin_session)]

[root@boysec ~]# tmux ls
kevin_session: 1 windows (created Tue Oct 2 19:38:36 2024) [135x34]

上面的命令可以拆分为下面三个:
[root@boysec ~]# tmux new -d -s kevin_session
[root@boysec ~]# tmux send -t kevin_session '/usr/local/bin/main' ENTER
[root@boysec ~]# tmux attach -t kevin_session

解释说明:
上面涉及到三个命令
第一个表示:在后台创建一个窗口,名为kevin_session
第二个表示:创建窗口的同时在终端里执行命令'/usr/local/bin/main',这里用单引号或双引号都可以。
第三个表示:进入到刚才创建的窗口kevin_session里面去

示例二
在创建窗口的同时,可以同时在窗口终端里连续执行两个或多个命令
[root@boysec ~]# su - kevin -c "tmux new -d -s haha && tmux send -t haha 'ifconfig' ENTER && tmux send -t haha 'hostname' ENTER"
kevin_session: 1 windows (created Tue Oct 2 19:38:36 2024) [135x34]
[root@boysec ~]# su - kevin
kevin@bobo:~$ tmux ls
haha: 1 windows (created Tue Oct 2 19:48:12 2024) [135x34]
kevin@bobo:~$ tmux a -t haha

如下一个实例:
在远程进行tmux的批量关闭和批量开启的shell脚本操作

1) 批量关闭/opt/ip.list文件里ip所在机器的main二进制进程(这个main进程是在tmux执行的,一直交互执行的那种)
[root@boysec ~]# vim /opt/script/6_main_stop.sh
#!/bin/bash
for i in $(cat /opt/ip.list)
do
ssh -p22 root@$i 'ps -ef|grep main|grep -v grep|awk -F" " "{print $2}"|xargs kill -9 >/dev/null 2>&1'
done

2)批量开启/opt/ip/list文件里ip所在机器的tmux窗口里的main二进制进程(即先关闭tmux,再创建tmux窗口以及在里面执行main进程)
[root@boysec ~]# vim /opt/script/7_main_start.sh
#!/bin/bash
for i in $(cat /opt/ip.list)
do
ssh -p22 root@$i 'ps -ef|grep tmux|grep -v grep|awk -F" " "{print $2}"|xargs kill -9 >/dev/null 2>&1'
ssh -p22 root@$i 'tmux new -d -s kevin_session && tmux send -t kevin_session '/usr/local/bin/main' ENTER'
done