第6章 工作流

2018-02-24 15:48 更新

第 6 章 工作流

就 tmux 自身来说,它只是一个添加了一些附加功能的另一个终端而已,只是显示了更多的终端会话。但是 tmux 让我们在这些会话里运行程序时更加方便,所以本章将会探讨一些常见、不常见的配置和命令,它们可能对你的日常工作有很大的帮助。你会学到管理面板和会话的高级方式,如何让 tmux 和 shell 一起工作,如何使用外部脚本扩展 tmux 命令,如何创建能执行数条命令的快捷键。我们先从管理窗口和面板开始。

6.1 高效使用面板和窗口工作

在本书中,你已经见到数种方式把 tmux 会话分割为多个面板和窗口。在本节,我们会学习使用面板和窗口工作的几种高级方式。

把面板变为窗口

面板很适合用来划分工作空间,但是有时我们需要把一个面板“弹出(pop out)”变为一个独立的窗口,这样看这部分内容就会更方便。tmux 有这样一个命令来实现这个功能。
在任意面板内,按下 PREFIX ! 键,tmux 就会依据当前面板创建一个新的窗口。

把窗口变为面板

有时候,我们需要合并一个工作空间,我们可以很简便地把窗口变为一个面板。为此,我们要提一下 join-pane 命令。

在“合并(join)”一个面板时,我们有可能把面板从一个会话移动到另一个会话里。此时需要指定源窗口和面板,后面跟随目标窗口和面板。如果不指定目标窗口,那么当前焦点窗口就会作为目标窗口。

下面我们通过创建一个带有两个窗口的 tmux 会话来演示:

$ tmux new-session -s panes -n first -d
$ tmux new-window -t panes -n second
$ tmux attach -t panes

现在,要把第一个窗口移动,作为第二个窗口的一个面板,按下 PREFIX : 键进入命令模式,然后输入这些:

join-pane -s 1

这句话的意思是“取出窗口(当窗口中有多个面板时则指取出面板,译者注)1 并把它添加到当前窗口”,因为我们没有指定一个目标窗口。

也可以使用这种方法来移动面板。如果第一个窗口有两个面板,可以像下面这样指定源面板,注意我们设置窗口从 1 开始编号,而面板从 0 开始编号:

join-pane -s 1.0

在这里,我们取出了第一个窗口的第一个面板然后把它添加到当前窗口。

更进一步地,甚至可以指定一个源会话,使用格式 -t [session_name]:[window].[pane] 指定一个目标窗口。

最大化和恢复面板

有时我们只是想让一个面板最大化显示一会,这样就可以细看它的内容,这时可以使用 break-pane 命令,然后再使用 join-pane 命令把它放回原处。这个操作做起来有些繁琐,因此我们编写一个脚本来实现这个功能。这是链接

首先,我们释放 UP 箭头键,把它设置为最大化命令。然后,创建一个新的快捷键 PREFIX UP 来触发这个 tmux 命令串,配置如下:

unbind Up
bind Up new-window -d -n tmp \; swap-pane -s tmp.1 \; select-window -t tmp

在配置里,我们创建了一个名为 tmp 的新窗口。通过给它命名,可以在子序列命令里调用它。当使用 -d 参数创建窗口时,tmux 会在后台创建这个窗口而不是把焦点转到这个窗口上。然后使用 swap-pane 命令选取已经选择的面板和临时窗口的已有面板进行交换。

要恢复窗口,只需要使用 swap-pane 命令把面板从临时窗口交换到原来的窗口里,选择源面板,然后杀掉临时窗口。我们把这个命令序列绑定到 PREFXI DOWN 键,就像这样:

unbind Down
bind Down last-window \; swap-pane -s tmp.1 \; kill-window -t tmp

由于它使用了 last-window 命令来返回源窗口,因此这个过程看起来就像是把一个面板啪的一下最大化了,然后又啪的一下把它恢复原位置,这个简单的例子说明了 tmux 高度灵活性。我们可以通过一个简单的快捷键来自动化实现一系列命令。

在面板里执行命令

在第 3 章,我们已经学习了如何使使用 shell 命令和 send-keys 在面板里启动程序,我们还可以让 tmux 在新建一个窗口或面板时自动执行命令。

假设有两台服务器,bums 和 smithers,分别是 web 服务器和数据库服务器。当启动 tmux 时我们想让 tmux 使用一个窗口的两个面板分别连接到这两台服务器上。

下面我们来创建一个新的名为 servers.sh 的脚本然后创建一个会话连接到两台服务器:

$ tmux new-session -s servers -d "ssh deploy@bums"
$ tmux split-window -v "ssh dba@smithers"
$ tmux attach -t servers

新建一个会话时,可以把要执行的命令作为最后一个参数传入到 tmux 中。在这里我们先是新建了一个会话然后在第一个窗口连接到 bums 服务器,然后从会话中分离出来。之后我们使用垂直分割切分窗口然后连接到 smithers 服务器。

这个配置有个副作用:从远程服务器上退出登录时,面板或窗口会关闭。

在 OS X 系统的同一目录下打开新面板

在 Linux 系统上创建 tmux 新的面板时,新面板使用的是当前面板的路径。但是在 OS X 系统上,新的面板会位于启动 tmux 会话时的那个目录。只需要做一点小小的工作,就可以在打开一个面板时捕捉它的工作路径然后自动地切换路径,就像 Linux 做的那样。

为此,我们使用 send-keys 命令来调用一个脚本把当前路径保存到环境变量中,然后这个脚本回调 send-keys 向拆分的窗口中发送命令,再把路径更改为环境变量中保存的那个路径。

首先,在主目录下创建一个名为 ~/tmux-panes 的新文件,写入以下内容:

TMUX_CURRENT_DIR=`pwd`
tmux split-window $1
tmux send-keys "cd $TMUX_CURRENT_DIR;clear" C-m

然后编辑 .tmux.conf 文件来调用这个文件做垂直和水平分割。这里使用 PREFXI v 键和 PREFXI n 键,以防覆盖了当前已有的分割快捷键。代码如下:

unbind v
unbind n
bind v send-keys " ~/tmux-panes -h" C-m
bind n send-keys " ~/tmux-panes -v" C-m

就像在之前讨论过的,我们需要使用 -v 参数来水平分割窗口,使用 -h 参数来垂直分割窗口。

最后,为 tmux-panes 脚本添加执行权限:

$ chmod +x ~/tmux-panes

在重新加载 .tmux.conf 配置文件后就可以分割面板了。

这种方法的弊端是它看起来有点 hack。它把命令输入到已有的 tmux 窗口并执行脚本。也就是说它只能在一个有命令提示符的窗口里才能被触发。所以,如果你的主窗口在运行 Vim,这个命令是不会有效的。即便是把 send-keys 命令换成 run-shell 命令也不会有效,因为新产生的 shell 也没有访问环境变的权限,因此它也就无法处理这个脚本了。但是这个脚本依然是一个比较方便的小技巧,通过自定义键盘快捷键,依然还保留了原始的命令。

6.2 管理会话

随着使用 tmux 越来越顺手,你会发现你会同时使用多个 tmux 会话。例如,你可能会为每个程序都开启一个 tmux 会话,这样就可以保持开发环境的相对独立。tmux 提供了多种特性能让你在管理这些会话时不会感到痛苦。

在会话间移动

单机上的所有 tmux 会话都通过一个服务器进行管理。也就是说我们能够在一台机器上就可以实现在会话之间来回移动。

下面来演示这个过程,我们会启动两个分离的 tmux 会话,一个名为 editor,它打开了 Vim,一个名为 processes 的会话则在运行 top 命令,命令如下所示:

$ tmux new -s editor -d vim
$ tmux new -s processes -d top

可以这样连接到 editor 会话:

$ tmux attach -t editor

然后按下 PREFIX ( 键进入前一个会话,按下 PREFIX ) 则可以跳转到下一个会话。

还可以使用 PREFIX s 键显示一个会话列表,这样就可以快速地从一个会话跳转到另一个会话。

你可以添加自定义的快捷键到 .tmux.conf 文件里来绑定 switch-client 命令。默认的配置应该是像这样:

bind -r ( switch-client -p
bind -r ) switch-client -n

如果你已经配置了多个工作空间,这样操作会极大地提高你的效率,而且它不需要分离会话再重新连接。

创建或连接到已有会话

到目前为止,我们学会了多种办法在任意时刻创建新的 tmux 会话。然而,事实上还可以判断一个 tmux 会话是否存在,如果存在的话就连接到它。

has-session 命令返回一个可以用在 shell 脚本里的布尔值。可以用它在 bash 脚本做一些类似这样的事情:

if ! tmux has-session -t remote; then
    exec tmux new-session -s development -d
    # other setup commands before attaching....
fi
exec tmux attach -t development

如果修改这个脚本让它通过参数读取会话名称,你就可以用它来连接或创建任意 tmux 会话。

在会话之间移动窗口

可以把一个会话的窗口移动到另一个会话里。如果已经在一个开发环境里打开了一个进程,现在想把它移动到另一个环境中,或者想合并工作空间时会非常有用。

move-window 命令被映射到快捷键 PREFIX . 键(英文句号键,译者注),这样可以很方便地把要移动的窗口作为当前焦点窗口,按下快捷键,然后输入目标会话即可。

下面演示一下这个过程,创建一个会话,一个名为 editor,一个名为 processes 分别运行了 vimtop 命令:

$ tmux new -s editor -d vim
$ tmux new -s processes -d top

我们会把 processes 会话的窗口移动到 editor 会话里。

首先,连接到 processes 会话,就像这样:

$ tmux attach -t processes

然后,按下 PREFIX . 键,然后在显示的命令行里输入 editor。

这会把 processes 会话里的唯一窗口移动出来,因此 processes 会话会自动关闭。如果连接到 editor 会话,就可以看到这两个窗口。

也可以使用 shell 命令来完成这个功能,因此不必在合并窗口时要连接会话。可以这样使用 move-window 命令:

$ tmux move-window -s processes:1 -t editor

这个命令的意思就是,把 processes 会话的第 1 个窗口移动到 editor 会话中。

6.3 tmux 和你的操作系统

既然 tmux 已经变成了你工作流的一部分,那么你肯定想让它和操作系统集成地越紧密越好。在本机,我们会向你展示多种方式,让你的 tmux 和操作系统一起工作。

使用一个不同的 Shell

在本书中,我们使用的 shell 环境都是 bash,但是如果你更喜欢 zsh,你依然可以使用所有的 tmux 优良特性。

可以在 .tmux.conf 文件里明确地设置默认的 shell 环境,就像这样:

set -g default-command /bin/zsh
set -g default-shell /bin/zsh

由于 tmux 只是一个终端复用器而并没有拥有独立的 shell,因此可以精确地指定使用哪个 shell。

默认启动 tmux

可以配置操作系统让它在打开一个终端时自动启动 tmux。通过使用会话名可以创建一个不存在的会话。

当 tmux 在运行时,它会把 TERM 变量设置为 screen 或者是 default-terminal 配置文件里的配置。可以在 .bashrc 文件(OS X 系统是 .bash_profile 文件)里使用这个变量来确定当前是否处于一个 tmux 会话中。我们在第 2 章配置了 tmux 终端为 screen-256color,因此可以使用这样一个脚本:

if [[ "$TERM" != "screen-256color" ]]
then
    tmux attach-session -t "$USER" || tmux new-session -s "$USER"
    exit
fi

如果没有在 tmux 会话里,我们会尝试连接到一个名为 $USER 的会话里,也就是当前用户名。可以把这个值替换为任意你想要的值,在这里使用用户名能帮助我们避免冲突。

如果会话不存在,tmux 会抛出一个错误,shell 脚本会把这个错误解释为 false 值。然后脚本会继续执行右侧的命令,也就是创建一个以用户名作为名称的会话。然后退出脚本。

当 tmux 会话启动时,它会再次运行配置文件,但是这次它会看到我们处于一个 tmux 会话中,因此就会略过这部分的后续代码,然后继续执行配置文件的其他配置,确保所有环境变量都已被配置。

现在,创建一个新的终端会话时,我们就自动地连接到一个 tmux 会话中。但是请小心,因为你每次打开新的终端窗口时都会连接到相同的会话,在任意终端窗口里输入 exit 命令都会关闭所有连接到会话的终端窗口!

把程序输出记录到日志里

有时需要把一个终端会话的输出记录到日志文件里。我们在之前已经讨论过如何使用 capture-panesave-buffer 命令来完成这些操作,但是实际上 tmux 可以通过 pipe-pane 命令把一个面板里的活动都记录到一个文本文件里。这很像许多 shell 里的 script 命令,使用 pipe-pane 命令可以选择打开或关闭这个功能,而且可以在一个程序已经运行之后再开始使用这个命令。

要激活这个功能,在命令模式输入命令 pipe-pane -o "mylog.txt"

-o 参数让我们打开了输出功能,也就是说如果再次发送相同的命令就可以把这个功能关掉。为了更方便地执行这个命令,我们把它添加到配置文件里并绑定一个快捷键。像这样:

bind P pipe-pane -o "cat >>~/#W.log" \; display "Toggled logging to ~/#W.log"

现在可以按下 PREFIX P 命令来控制打开日志功能了。多亏了 display 命令(display-message 命令的简写),我们可以在状态栏看见日志文件名。display 命令和状态栏一样能访问相同的变量。

在状态栏添加电池电量显示

如果你在笔记本电脑上使用 tmux,你可能想在状态栏里显示电池剩余电量,尤其是终端在全屏状态下运行的时候。幸亏有 #(shellcommand) 变量能够让这个事情变得相当容易。

现在我们把电池状态添加到配置文件里。我们抓取了一个简单的 shell 脚本,它能够获取剩余电池电量并把它写入主目录下名为 battary 的文件里。要让 tmux 能够使用这个脚本,要赋予它执行权限。在终端里运行这些命令:

$ wget --no-check-certificate \
https://raw.github.com/richoH/dotfiles/master/bin/battery
$ chmod +x ~/battery

如果在终端里运行这个命令:

$ ~/battery Discharging

我们就会看到电池剩余电量的百分比。可以让 tmux 通过 #(<command>) 命令把它显示在状态栏里。所以,要在时钟之前显示电池电量,需要这样修改配置文件里 status-right 这一行:

set -g status-right "#(~/battery Discharging) | #[fg=cyan]%d %b %R"

重新加载 .tmux.conf 文件时,电池的剩余电量就会显示出来。

要想在电池充电时获取它的状态,需要执行这个命令:

$ ~/battery Charging

然后根据上面的方法,可以把这个命令添加到状态栏里。这部分的工作留给你来完成。

使用这种方法更深度地定制状态栏。只需要编写自己的脚本然后返回你想要显示的任意值,然后把它扔到状态栏里。

6.4 接下来做什么?

你已经学会了 tmux 的基础你就可以做很多事情,而且你现在已经对不同的配置有了一定的经验。tmux 的用户手册,可以在终端里获取,命令如下:

$ man tmux

这里面有完整的配置选项列表和所有 tmux 可用命令。

别忘了 tmux 本身也是在快速进化之中。下一个版本将会带来新的配置选项,会为你带来更高的灵活性。

现在你已经把 tmux 集成到你的工作流之中了,你可以尝试发掘一些其他的常用技术。例如,可以一起使用 tmux 和 Vim 来创建更高效的开发环境。你还可以在 tmux 会话里使用 irssi(一个终端界面的 IRC 客户端)和 Alpine(一个基于终端的邮件应用),每个程序占用你的一个面板,和你的文本编辑器并排排列,或是让它们运行在后台窗口里。然后你可以从会话中分离出来,过段时间再连接到 tmux 会话中,一如既往。

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号