# 第 4 章 文本和缓冲区 在日常工作过程中,你使用复制和粘贴的次数远超你的想象。使用 tmux,可以直达你需要终端输出缓冲区屏幕回滚的地方来查看缓冲区里那些已经不在屏幕范围的内容。你还可能需要复制一段文字然后把它复制到一个文件或另一个程序里。本章将会介绍如何管理 tmux 会话里的文本。你会学习到如何使用键盘操作 tmux 的输出缓冲区,如何使用多重粘贴缓冲区,以及如何使用系统剪贴板。 ## 4.1 使用复制模式滚动输出 在终端里使用程序时,程序的输出常由于内容过多而不停滚动以致超出了屏幕范围。使用 tmux,可以通过键盘来向前翻动输出缓冲区,这样就可以看到错过的内容。这个功能在运行测试或是审阅日志文件时尤其有用。 按下 `PREFIX [` 键会进入复制模式,然后就可以使用光标移动键在屏幕里移动光标。默认地,光标移动键是箭头键。在第 2 章,我们在配置文件里使用了 Vim 模式的移动键,这样就可以在窗口之间移动以及重绘面板大小时不用把手移出键盘的主操作区。tmux 里有一个使用 vi 模式操作缓冲区的功能。要开启这个功能,请把下面这行代码添加到你的 `.tmux.conf` 文件里: ``` setw -g mode-keys vi ``` 这个配置可以使用 `h`,`j`,`k` 和 `l` 键在缓冲区里移动。要离开复制模式,只需按下 `ENTER` 键(回车键,译者注)。但是一次移动一个字符显然是不怎么高效的。那么既然我们开启了 vi 模式,我们也可以使用其它 vi 的快捷键在缓冲区里移动。 例如,可以使用 `w` 键跳到下一个单词,使用 `b` 键回跳一个单词。也可以使用 `f` 键,后面跟随任意字符来跳到当前行的指定字符,使用 `F` 键回跳。 ### 在缓冲区里快速移动 当缓冲区里输出了多个页面时,在滚屏之间使用光标移动并不是很高效。取代单词之间移动或是字符之间移动的一种方式,就是在缓冲区里一页一页的滚动,或者是跳转到缓冲区的顶部或底部。 可以使用 `Ctrl b` 向上翻滚一屏或是使用 `Ctrl f` 向下翻滚一屏,使用 `g` 键跳转到缓冲区历史的最顶部,也可以使用 `G` 键跳转到缓冲区历史的最底部。 ### 查找缓冲区 如果知道要查找什么内容的话我们就不必一页页的在数百行内容之间浏览。在复制模式里按下 `?` 键,可以向上查找短语或关键词。只需按下 `?` 键,输入要查询的短句,然后按下 `ENTER` 键就会跳转到第一个匹配的短句。然后按下 `n` 键跳转到下一个匹配,或者按 `N` 移动到上一个匹配。 要向下查找,你需要按下 `/` 键而不是 `?` 键。按下 `n` 键会跳转到下一个匹配而按下 `N` 键会跳转到上一个匹配。 学习以这种方式在缓冲区之间移动会极大地加快你的速度。输入想要移动到的单词而不是使用箭头来移动会更快,尤其是在查找日志文件的输出时会更明显。 这只是一些使用缓冲区的基本知识。下面我们来学习如何复制一个面板的文本然后把它粘贴到另一个面板中。毕竟,这是 tmux 的复制模式。 ## 4.2 复制和粘贴文本 在输出缓存区里移动和查找内容只是程序的一半(意为部分功能,译者注)。我们还会经常需要复制一些文本。tmux 的复制模式提供了这个机会,它能让我们选择并复制文本到一个粘贴缓存区,这样就可以把它复制到任何地方。 要复制文本,需要先进入复制模式然后把光标移动到要选择的文本的开始处。然后按下 `SPACE` 键(空格键,译者注)然后移动光标到文本的尾部。当我们按下 `ENTER` 键后,这段被选择的文本就会被复制到一个粘贴缓存区中。 要粘贴刚才捕捉的内容,则切换到粘贴模式然后按下 `PREFIX ]` 键。 下面我们来学习一些特定的方式来从主输出缓冲区里复制和粘贴文本。 ### 捕捉面板 tmux 有一个非常方便的快捷键可以把整个面板的可视内容全部复制到一个粘贴缓存区里。只需要按下 `PREFIX :` 键进入命令模式然后输入 ``` capture-pane ``` 接下来就可以使用 `PREFIX ]` 键把内容复制到当前焦点会话里。 ### 显示并保存缓存区 显示粘贴缓存区的内容,只需要在命令模式里使用 `show-buffer` 命令,或者是在终端会话里使用如下命令: ``` $ tmux show-buffer ``` 使用 `save-buffer` 命令可以把缓存区保存到一个文件里,而且这是实时的保存。事实上,可以捕捉当前缓存区并保存到一个文本文件,就像这样: ``` $ tmux capture-pane && tmux save-buffer buffer.txt ``` 或者在命令模式里使用 `capture-pane; save-buffer buffer.txt` 命令。当然,如果你想的话,可以把这个操作定义为一个快捷键。 ### 使用多重粘贴缓存区 tmux 保留了一个粘贴缓存区的栈空间,也就是说可以多次复制文本而无需替换缓冲区里已有的内容。这比操作系统提供的传统剪贴板要便捷得多。 每次我们复制一些新的文本时, tmux 就会创建一个新的粘贴缓存区,把新的缓存放在栈的最顶端。 为了更清楚的描述,下面我们来创建一个新的 tmux 会话然后打开一个文本编辑器例如 Vim 或 Nano。在编辑器里,输入以下句子,每句话占用一行空间: ``` First sentence is first. Next sentence is next. Last sentence is last. ``` 现在复制一些文本到粘贴缓存区里。按下 `PREFIX [` 键进入复制模式。移动到第一个句子的开始处,按下 `SPACE` 键开始选择文本,移动到第一句话的末端,然后按下 `ENTER` 键选择整句话。然后对第 2 句话和第 3 句话重复刚才的操作。 每次我们复制文本时,tmux 都创建一个新的缓存区。可以通过 `list-buffers` 命令查看这些缓存区。如下: ``` 0: 22 bytes: "Last sentence is last." 1: 22 bytes: "Next sentence is next." 2: 24 bytes: "First sentence is first." ``` 按下 `PREFIX ]` 键会总是粘贴第 0 个缓存内容,输入 `choose-buffer` 命令来选择一个特定的缓存区然后把它的内容粘贴到焦点面板中。 把当前窗口分为两半,然后在第 2 个面板中打开 Nano 程序,然后进入命令模式输入以下命令: ``` choose-buffer ``` 你会看到一个列表显示如图12(选择一个文本缓存并插入)所示。你可以选择列表中的任意一个,按下 `ENTER`,文本就会被插入到指定的面板。 ![document/2015-09-09/55efaba410fd3](https://box.kancloud.cn/document_2015-09-09_55efaba410fd3.jpg) 图12 - 选择一个文本缓存并插入 这种方式来管理多重文本非常棒,当你在使用一个字符界面的环境却无法进入剪贴板时尤其有用。 这些缓存会在 tmux 会话的运行过程中被共享,因此我们可以把粘贴缓存中的内容从一个会话复制到另外一个里。 ### 自定义复制和粘贴快捷键 如果你习惯使用 Vim 而且用复制和粘贴命令比较频繁,你可以在你的配置文件里重新定义这两个命令的快捷键。例如,可以把 `ESCAPE` 键(ESC 键,译者注)定义为进入复制模式,使用 `y` 键(yank,复制,译者注)把文本复制到缓存区,使用 `v` 键进入视图模式(Visual mode,译者注)选择文本,然后使用 `p` 键(paste,译者注)粘贴文本,配置如下: ``` unbind [ bind Escape copy-mode unbind p bind p paste-buffer bind -t vi-copy 'v' begin-selection bind -t vi-copy 'y' copy-selection ``` 如果你经常在窗口和面板之间大量使用复制和粘贴操作的话,这样的配置会极大程度地提高你的工作效率,而且这些快捷键都是你非常熟悉的 Vim 快捷键。 ## 4.3 在 Linux 使用剪贴板 使用 `xclip` 实用工具可以集成 Linux 系统上的剪贴板,这样你就可以在不同程序之间更加便捷地进行复制和粘贴操作。 首先你需要安装 `xclip` 工具。在 Ubuntu 系统上,使用如下命令: ``` $ sudo apt-get install xclip ``` 然后通过 `xclip` 来使用 tmux 的 `save-buffer` 和 `set-buffer` 命令。 要把当前缓存区的内容复制到系统剪贴板,我们在 `.tmux.conf` 文件里添加如下命令: ``` bind C-c run " tmux save-buffer - | xclip -i -sel clipboard" ``` 这个配置定义了 `PREFIX CTRL-c` 快捷键来捕获当前缓存区的内容然后通过管道输出到 `xclip` 程序里。可以把下面这个命令也添加到配置文件里,这样就可以使用 `PREFIX CTRL-v` 快捷键把系统剪贴板的内容复制到 tmux 会话中来: ``` bind C-v run " tmux set-buffer \"$(xclip -o -sel clipboard)\"; tmux paste-buffer" ``` 这个命令会把 `xclip` 的内容输出到一个新的 tmux 缓存区里然后把它粘贴到当前的 tmux 窗口或面板。 ## 4.4 使用 OS X 剪贴板命令 如果你是一个 Mac 用户,你会比较熟悉 OS X 的命令行剪贴板工具 `pbcopy` 和 `pbpaste`。这些简单的工具使得你在使用剪贴板时更加方便。`pbcopy` 命令把文本复制到系统剪贴板,`pbpaste` 命令把内容粘贴出来。例如,你可以一起使用 `pbcopy` 和 `cat` 命令,这样就可以很方便地把 `.tmux.conf` 文件的配置复制到剪贴板,然后可以把它粘贴到电子邮件或是 Web 上,就像这样: ``` $ cat ~/.tmux.conf | pbcopy ``` 这样操作文本确实很方便,但是 tmux 并没有权限访问这些工具,在 tmux 会话里并不能直接使用它们。可以使用一个由 Chris Johnsen 编写的包装程序(wrapper program)来突破这个限制。[Github 链接](https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard) 要使用这个包装脚本,首先克隆这个项目到本地然后编译它。代码如下: ``` $ git clone https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard.git $ cd tmux-MacOSX-pasteboard/ $ make reattach-to-user-namespace ``` 然后,把文件移动到 `PATH` 路径下的某个位置,例如 `/usr/local/bin`,代码如下: ``` $ sudo mv reattach-to-user-namespace /usr/local/bin ``` 最后,配置 tmux 来使用这个包装程序,把下面这行命令添加到 `.tmux.conf` 文件中: ``` set -g default-command "reattach-to-user-namespace -l /bin/bash" ``` 这个配置会让 tmux 的新窗口能够通过包装脚本加载 Bash shell。如果你使用的是其它 shell,你需要在配置里修改它的指定路径或命令。 重新加载配置文件之后,就可以使用 `pbcopy` 命令了。这样还会有个额外的好处,就是可以把当前 tmux 缓存区的内容添加到系统剪贴板里了,命令如下: ``` $ tmux show-buffer | pbcopy ``` 或者可以粘贴剪贴板里的内容,像这样: ``` $ tmux set-buffer pbpaste; tmux paste-buffer ``` 这就意味着我们可以创建键盘快捷键来做这个操作,就像我们在 4.3 章节里所做的那样。但是很不幸,我们使用的包装程序无法和 tmux 的 `run` 命令一起使用。好在有解决方案,可以在 `pbpaste` 和 `pbcopy` 命令前添加包装程序的前缀。所以,为了支持复制功能,我们在配置文件里要这样定义一个快捷键: ``` bind C-c run "tmux save-buffer - | reattach-to-user-namespace pbcopy" ``` 同样,要支持从系统剪贴板粘贴内容,需要把下面这个超级长的命令添加到配置文件里,要注意,这个命令必须都在同一行里: ``` bind C-v run "tmux set-buffer $(reattach-to-user-namespace pbpaste); tmux paste-buffer" ``` 这样就用一个简单的办法解决了一个比较复杂的技术问题。 ## 4.5 接下来做什么? 要想在缓存区和剪贴板之间来移动文本,你可以在一个没有剪贴板的环境中创建一个,比如当你登录了服务器的控制台或是一个没有图形界面的终端。能够在一个很长的控制台的输出历史之间来回滚动会帮我们做很多事情。仅仅这个功能,就值得你在服务器上安装 tmux。 现在我们对 tmux 里如何移动,切换窗口或面板有了比较详细的了解,可以在日常中使用 tmux 工作了。对很多开发人员来说,结对编程也是日常工作的一部分。接下来我们会学习如何使用 tmux 和其他开发人员合作。 ### 以备参考 #### 快捷键 |快捷键 | 描述| |---|---| |`PREFIX [`| 进入复制模式 | |`PREFIX ]`| 粘贴当前缓存区的内容 | |`PREFIX =`| 列出所有粘贴缓存区并粘贴选中的缓存内容 | #### 复制模式移动键(VI 模式) |命令 | 描述| |---|---| |`h, j, k, 和 l`| 移动光标,分别表示向左,向下,向上和向右 | |`w`| 以一个单词为单位向前移动光标 | |`b`| 以一个单词为单位向后移动光标 | |`f 后跟随任意字符`| 移动光标到指定字符的下一个匹配位置 | |`F `后跟随任意字符| 移动光标到指定字符的前一个匹配位置 | |`CTRL -b`| 向上翻滚一个屏幕的位置 | |`CTRL - f`| 向下翻滚一个屏幕的位置 | |`g`| 跳转到缓存区的顶部 | |`G`| 跳转到缓存区的底部 | |`?`| 在缓存区内向后查找 | |`/`| 在缓存区内向前查找 | #### 命令模式 |命令 | 描述| |---|---| |`show-buffer`| 显示当前缓存区内容 | |`capture-pane`| 捕捉指定面板的可视内容并复制到一个新的缓存区 | |`list-buffers`| 列出所有的粘贴缓存区 | |`choose-buffer`| 显示粘贴缓存区并粘贴选择的缓存区内的内容 | |`save-buffer [filename]`| 保存缓存区的内容到指定文件里 |