Contents

tmux 简易指南(二)

上次简单介绍了 tmux 的一些基本概念和用法,能让我们很快的上手使用。但想用的顺手我认为还需要做些改进。

所以,本篇主要分享自己目前在用的 tmux 配置,并附上详细说明原因,自己以后查阅也方便。

在分享前

先介绍几个概念,tmux 中有几种常见配置:

  • 快捷键(key):可以通过 bind-key 命令来配置,比如 tmux bind-key S choose-tree 意思是绑定 S 用来触发 choose-tree 这个 tmux 命令(当然,要先按 prefix key ),效果和直接再 tmux session 中输入 tmux choose-tree 是一样的。
  • 变量(option):比如 tmux set-option history-limit 10000 是设置 tmux 的历史长度

这些配置都可以放到 ~/.tmux.conf 这个文件下,tmux 第一次启动时,会加载这些配置。

在运行 tmux 时,可以通过 prefix key + :,然后输入 source-file ~/.tmux.conf 手动重新加载配置。不过呢,和 shell 一样,tmux 加载配置只是修改配置中对于的变量,对于旧的没被覆盖的配置,还是会在的。

所以有时候我会 tmux kill-server 直接杀掉 tmux 进程,再重新打开 tmux 来保证配置加载的完整性。

放一张配置完的图,也是上一篇的第一张图 https://i.loli.net/2019/02/06/5c5a9390261f6.jpg

开始吧

开胃菜

1
2
3
# Common config
set-option -g set-titles on
set-option -g set-titles-string "#S / #W"

tmux 默认不会改 Terminal 的 title,所以在终端软件的 tab 名字里面只有 “tmux”,这对于同时要开很多个 tab 的人来说很不友好。所以我开启了 title ,并把它的格式改为 当前 session 名字 / 当前 window 名字

1
set-option -g repeat-time 1000  # default is 500

tmux 在执行连续的快捷键时(比如切换 pane ),默认只有 500ms 的等待时间,超过这个时间,就会退回到正常输入模式,有时候稍微按得慢一点(也可能是我手残),就要重新按 prefix key 来重新输入快捷键,所以我把他改为 1s

1
set-option -g history-limit 10000

默认的历史行数比较小,改大一点,看程序日志的时候不至于被吃掉。(有时候我会吧 tmux 当成临时的 supervisor 进程管理解决方案)

1
2
3
# Terminal type configuration
set-option -g default-terminal "screen-256color"
set-option -ga terminal-overrides ",xterm-256color:Tc"

tmux 中的颜色可能和 shell 中的有所偏差,使用如上设置使得颜色能正常显示。

副菜

作为 Vim 党,当然希望 tmux 中也能用到一些 Vim 类的快捷键。

1
set-window-option -g mode-keys vi

这样就可以在滚动模式(copy-mode)中,使用一些 Vim 键位了。

不过我觉得这还不够 Vim ,还需要进一步的设置。

1
2
3
4
5
# Move & Copy while scrolling
bind-key -T copy-mode-vi v send-keys -X begin-selection
bind-key -T copy-mode-vi y send-keys -X copy-selection-and-cancel
bind-key -T copy-mode-vi u send-keys -X halfpage-up
bind-key -T copy-mode-vi d send-keys -X halfpage-down

浏览历史的时候,可以用 v 选择,y 复制,u/d 翻页。

1
2
3
4
5
# Select pane
bind-key -r j select-pane -D
bind-key -r k select-pane -U
bind-key -r h select-pane -L
bind-key -r l select-pane -R

使用 j,k,h,l 来选择 pane

1
2
3
4
5
6
7
8
9
bind-key -r M-j resize-pane -D
bind-key -r M-k resize-pane -U
bind-key -r M-h resize-pane -L
bind-key -r M-l resize-pane -R

bind-key -r M-J resize-pane -D 5
bind-key -r M-K resize-pane -U 5
bind-key -r M-H resize-pane -L 5
bind-key -r M-L resize-pane -R 5

使用 Meta + j,k,h,l 来调整 pane 大小(如果使用 iTerm 的话,可能需要把 Option 键位设置成,Esc+ 键)

主食

显示当前运行程序

程序输出日志多了,不清楚当前执行的程序是什么了?

1
2
3
4
5
6
7
# Pane border
set-option -g pane-border-status top
# Show current cmd
if-shell "uname | grep -q Darwin" \
  "set-option -g pane-border-format ' #P: #(sleep 0.25; ps -t #{pane_tty} -o args= | tail -n 1) '"
if-shell "uname | grep -q Linux" \
  "set-option -g pane-border-format ' #P: #(sleep 0.25; ps -t #{pane_tty} -o args= --sort=etime | head -n 1) '"

我的方案是打开 panetitle ,然后把 title 设置为当前执行的程序 cmd

原理为使用 ps 命令显示当前 tty 下执行的进程,并显示 cmd

然而,这可能会打出多个进程,因为进程和它的子进程会被同时打印出来。比方说用 bash 执行了一个 shell 脚本,脚本内容如下:

1
sleep 3600

如果只显示父进程,那么结果是 bash foo.sh,并不能清楚知道脚本执行到哪了,我希望看到的是 sleep 3600,所以需要根据 ps 得到的结果,来获取子进程的 cmd

由于平台不一样 ps 的实现也是不一样的。经过测试,macOSlinuxps 打印出来的子进程顺序是相反的,所以两个平台要区分开来对待:Darwin 内核(也就是 macOS 的内核)的用 tail -n 1linux 内核用 head -n 1 来获取子进程的 cmd

关于命令中的 sleep 0.25 主要是因为,有时候会出现 title 失效不更新的情况,加入该命令可以极大的减少该情况的出现。

快速切换 session

同时打开的 session 有点多时,会很不方便切换。虽然 tmux 提供了 prefix key + s 来显示所有的 session 并且可以用方向键来选择切换。

但还是不够好用,我想要的是像 Context 一样的通过用 session name 来切换的功能,并且提供 fuzzy search,并在我打错字个别字时也能正常切换。

这样我只要知道 session name ,闭着眼睛也能切换 session ,不用盯着屏幕看,不用在意 session 的排序位置。

这个功能依赖 fzf 中的 fzf-tmux 程序

1
2
bind-key s run-shell "tmux list-sessions -F \"##S\" | fzf-tmux | xargs tmux switch -t; true"
bind-key S choose-tree -s

按下 prefix key + s ,你将会看到 tmux 跳出一个额外的 pane ,上面显示了所有的 session name。

https://i.loli.net/2019/02/23/5c70da5394a18.jpg

你可以通过方向键选择,也可以直接输入 session name,回车即可跳转到该 session

https://i.loli.net/2019/02/23/5c70da53a2813.jpg

有时候 tmux 自带的 choose-tree 也挺有用的,所以我把它的键位移到到了 prefix key + S

沙拉

tmux 滚动模式的坑

tmux 在进入 copy mode (查看 pane 历史)时,会暂停读取在 pane 中运行的 ttystdoutstderr ,如果 tty 中的程序继续往标准输出中写入,那么操作系统的 tty 缓存最终会被填满。此时程序再写入时,为避免缓冲区溢出,操作系统会会阻塞该 IO 操作。此时该程序很有可能被阻塞,假如这个程序是个提供 web 服务的 server,那么它将停止响应所有的请求。

目前 tmux 官方没有打算解决这个问题(可能是 tmux 实现原因),但有方法可以绕过这个坑。

1
2
Show history as scrollable view
bind-key F capture-pane -S -10000 -b capture\; show-buffer -b capture\; send-keys G\; delete-buffer -b capture

原理是捕获当前历史,保存到 tmux buffer 中,然后再显示 buffer 的内容。这个方案和 copy mode 的区别就是会丢失 shell 中的颜色。(tmux show-buffer 命令不支持显示颜色)

保存历史到文件

tmux 中的历史不能像普通的 shell 一样,可以通过滚动终端类软件的自带的历史记录来截取日志,假如要截取的日志很长一段,没法直接在一个页面中打印出来怎么办?

1
bind-key P command-prompt -p 'save history to filename:' -I '~/tmux.history' 'capture-pane -S -10000 -b capture ; save-buffer -b capture %1 ; delete-buffer -b capture'

按下 prefix key + P 即可把历史记录保存到文件中,然后想怎么分析就怎么分析 xD 。

甜点

继承当前目录

打开一个新的 panewindow ,当前目录不会继承自上一个 pane ,每次都会在用户目录,挺不方便的。

1
2
3
4
5
6
7
# -- Split with current direcotry --
# Split panes horizontal
bind-key '%' split-window -h -c '#{pane_current_path}'  
# Split panes vertically
bind-key '"' split-window -v -c '#{pane_current_path}'
# Create new window
bind-key c new-window -c '#{pane_current_path}'

插件管理

tpmtmux 中的插件管理器,可以用来安装管理自己喜欢的 tmux 插件,非常方便。

安装

1
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm

推荐些我用的插件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# =---- Plugins ----=
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'XSAM/tmux-themepack'

# Continuous saving tmux environments
set -g @plugin 'tmux-plugins/tmux-resurrect'
# set -g @plugin 'tmux-plugins/tmux-continuum'
# =---- Plugins ----=

# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)
run -b '~/.tmux/plugins/tpm/tpm'
  • tmux-plugins/tmux-sensible:帮你配置了一些常规的设置
  • XSAM/tmux-themepack: 我改的主题(自认为还挺好看的 xD)
  • tmux-plugins/tmux-resurrect: 能保存 tmuxsession 结构,这样关机重启之后,能马上恢复

咖啡、茶

我维护了一个叫 kind-lazy 的项目,提供一些程序的快速安装脚本,其中也有本文提到的 tmux 配置。

快速安装 tmux

1
2
3
git clone https://github.com/XSAM/kinda-lazy.git
cd kinda-lazy/tmux
bash lazy_install.sh

喜欢的话可以给项目点个 Star 🚀

Enjoy!

Ref