<audio id="audio" title="29 |  1+1>2，灵活的工具组合及环境让你的工作效率翻倍" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/d6/33/d6814fc2f97bc90572b604611e2aca33.mp3"></audio>

你好，我是葛俊。今天，我们来聊一聊工具的组合和环境。

在前面几篇文章，我与你介绍了很多工具，有开发工具，也有跟开发不直接相关的工具。毫无疑问，这些工具都很好用。但，如何配置这些工具，才能真正发挥它们的价值，提高我们的研发效能呢？

我们都很清楚，工具的使用离不开具体的工作环境。如果我们在环境中使用的各个工具是割裂开的话，不仅会提高我们的学习成本、记忆成本，还会有工具间交互的衔接问题。所以，用好这些工具，我们还需要做两件事：

- 尽量把工具无缝集成，解决工具切换不顺畅的问题；
- 减少并优化常用的工作入口，从而提高工具一致性，降低使用多个工具时的心智负担。

只有这样，我们才能把工具配置成一套好的环境，真正聚焦在产生价值的工作上，发挥工具提升研发效能的作用，实现1+1&gt;2的效果。

所以接下来，我会从工具集成和提高工具一致性两个方面，与你介绍如何把多个工具组合成为高效的工具环境。

## 工具的集成

工具的集成，最值得优化的情况包括两种：一是，使用管道（Pipe）对命令行工具进行集成；二是，对集成开发工具环境（IDE）进行配置，让IDE和周边工具集成。

### 使用管道（Pipe）对命令行工具进行集成

其实，我在前面的文章中已经使用过管道很多次了，只是使用场景比较简单而已。

比如，在[第24篇文章](https://time.geekbang.org/column/article/150779)中，我使用命令curl -s &lt;github_url&gt; | vim -，来查看GitHub上某个用户的代码仓情况，通过管道把curl命令的输出传给VIM，以方便我在VIM中查看较长的输出。

```
&gt; curl -s https://api.github.com/users/jungejason/repos | vim -
Vim: Reading from stdin...

## 进入VIM窗口，显示所有repo的array
[
  {
    &quot;id&quot;: 213849635,
    &quot;node_id&quot;: &quot;MDEwOlJlcG9zaXRvcnkyMTM4NDk2MzU=&quot;,
    &quot;name&quot;: &quot;counter-redux-sample&quot;,
    &quot;full_name&quot;: &quot;jungejason/counter-redux-sample&quot;,
    &quot;private&quot;: false,
    &quot;owner&quot;: {
      &quot;login&quot;: &quot;jungejason&quot;,
...

```

这个案例使用了一次管道，比较简单。

而高效使用管道，首先是**使用多个管道灵活地把多个工具连接起来**。一个常见的场景是，grep和xargs命令的组合。比如，ps aux | grep vim | grep -v grep | awk ‘{print $2}’ 可以找到我当前运行的VIM程序并将其关闭。

```
ps aux | grep vim | grep -v grep | awk '{print $2}' | xargs kill

```

整个管道链是这样工作的：

- ps aux打印所有运行程序，每行打印一个。
- grep vim显示包含vim的那些行。
- grep -v grep表示过滤掉包含grep的那一行。这是因为，上面一步grep vim命令中包含“vim”字样，所以它也会被ps aux显示出来，我们需要将其过滤掉。
- awk ‘{print $2}’ 表示抽取出输出中的PID，也就是进程ID。
- xargs kill 接收上一步传过来的PID，组成命令kill <pid>并执行，杀死vim进程。</pid>

上面管道每一步的执行输出，如下所示：

```
&gt; ps aux
USER               PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
_windowserver      228  14.9  2.3 13044220 382952   ??  Ss    9Oct19 679:18.68 /System/Library/PrivateFrameworks/SkyLight.framewo
jasonge          49393  14.4  2.1  5586816 356860   ??  S    Sun12AM 583:37.42 /Applications/XMind.localized/XMind.app/Contents/F
jasonge          49371   9.7  0.4  4826572  64204   ??  S    Sat11PM 167:30.01 /Applications/XMind.localized/XMind.app/Contents/F
...

&gt; ps aux | grep vim
jasonge          31383   0.0  0.0  4310340   4740 s001  S+    9:57AM   0:00.11 vim
jasonge          32304   0.0  0.0  4268056    704 s004  R+   10:17AM   0:00.00 grep vim

&gt; ps aux | grep vim | grep -v grep
jasonge          31383   0.0  0.0  4310340   4740 s001  S+    9:57AM   0:00.11 vim

&gt; ps aux | grep vim | grep -v grep | awk '{print $2}'
31383

&gt; ps aux | grep vim | grep -v grep | awk '{print $2}' | xargs kill

```

这是使用多个管道的一个具体例子。在工作中，你可以根据实际情况，使用管道来封装常用的工具。

**在命令行中高效使用管道的第二个重要方法，是使用模糊查询工具（Fuzzy Finder)。**

常见的模糊查询工具，有 fzf、pick、selecta、ctrlp和fzy等。其中，最有名的应该就是fzf（你可以再回顾下[第28篇文章](https://time.geekbang.org/column/article/156024)中关于这个工具的介绍）。

在管道中使用模糊查询工具，可以大幅度提高使用体验。

在开发工作中，我们常常需要对文本进行搜索和过滤，一般会使用grep来实现。这个操作虽然简单，但频率非常高，很值得优化。比如，上面提到的杀死vim进程的操作。我们使用ps aux命令列举出所有进程之后，需要使用多个grep命令来进行搜索过滤，最终查找到我们需要的那一行。

在这个操作中，grep过滤命令常常会比较复杂，为了达到正确的过滤效果，我们可能要调试多次才能找到正确的grep命令。另外，还可能会出现比较难以使用grep过滤的情况，比如有多个vim进程同时打开时，使用grep选出其中一个就不是很方便。

这时，使用模糊查询工具就很方便了。在我看来，**模糊查询工具的本质，就是交互式的文本过滤工具**。它接收文本，然后提供界面让用户输入查询条件，并在用户输入的同时实时过滤。当用户找到需要的结果回车确认后，输出结果文本，使用起来很自然。

以杀死vim进程的操作为例，我可以使用ps aux | fzf命令把进程列表发送给fzf，fzf首先会列举出所有进程供我搜索过滤。随着我的输入，fzf进行实时过滤，去除不符合条件的那些行。在我输入了’vim后，就只剩下几行结果了。这时，我可以使用上下键进行选择，回车之后fzf就会把这一行内容输出到stdout中。

可以看到，整个过程中我可以实时调整搜索条件，免去了使用grep需要多次调整参数的过程，使用起来非常便捷。

<img src="https://static001.geekbang.org/resource/image/15/2b/158b70bfdde5ab3b1b940b1abe21932b.gif" alt="">

当然，除了把过滤结果输出到stdout外，也可以通过管道把输出传给其他工具，比如传给awk和kill，来实现杀死vim进程的目的，具体的命令是ps aux | fzf | awk ‘{print $2}’ | xargs kill。

<img src="https://static001.geekbang.org/resource/image/56/6a/561acdf6734212a36edd91295a00876a.gif" alt="">

可以看到，这个命令和前面使用grep的命令差不多，唯一区别是把之前命令中的两个grep替换成了fzf。但正是这个区别，把中间的非交互文本过滤变成了交互式过滤，效果非常好。

实际上，你还可以更进一步，把这个使用fzf的命令保存为一个shell脚本，成为一个交互式的kill命令。如下所示：

```
#!/bin/bash

ps aux | eval &quot;fzf&quot; | awk '{print $2}' | xargs kill

```

<img src="https://static001.geekbang.org/resource/image/59/da/59ce8d67bffa2e15e68665399511bdda.gif" alt="">

从非交互文本过滤到交互文本过滤的简单转变，着实可以为我们带来意想不到的巨大方便。比如：

- 在切换工作路径时，可以用find命令列举出当前文件夹的所有子文件夹，然后进行交互式的过滤，最后切换路径。
- 在使用apt或者brew安装软件包时，可以用来过滤可用软件包，方便软件包的选择。

它甚至可以与一些网站服务对接，比如这个使用[fzf和caniuse服务集成](https://sidneyliebrand.io/blog/combining-caniuse-with-fzf)的例子，就很酷。此外，[fzf官网上](https://github.com/junegunn/fzf/wiki/examples)还有很多类似的脚本例子，推荐你去看看。

### IDE和周边工具集成

我们的目标是能在IDE中进行常见的软件研发活动，从而减少工具的切换。

具体来说，基本的IDE集成功能包括：

- 编码；
- 构建；
- 实时检查语法错误；
- 实时检查编码规格；
- 运行单元测试，并在测试输出中点击文件名进行跳转；
- 在本地运行服务，并可以设置断点进行单步调试；
- 连接远程服务进行单步调试。

这些功能比较常见，你可以根据自己使用的IDE在网上搜索相关资料，我就不再赘述了。接下来，我会重点与你分享些不是那么传统，但非常有效的集成功能。

**首先，是IDE跟命令行工具的集成。**

命令行里有很多的工具，所以IDE只要集成了命令行终端，就可以把它们一次性都集成了进来，所以效果非常好。

这里的集成有两个层次：

- 第一个层次是，终端窗口成了IDE应用的一个子窗口，这样使用比独立的终端窗口使用起来更方便。比如，可以使用快捷键方便地打开、关闭集成终端窗口。方便窗口管理。
- 第二个层次是，终端子窗口和IDE其他部分的一些交互。这个层次更深，也更重要，可以让我们更好地在IDE中使用命令行工具，是我们要留意的。我**推荐你在你所用的IDE文档中，查看有哪些第二个层次的集成**。

以VS Code为例，属于第二层次的集成有以下四种。

**第一种**，在IDE中拖动文件夹或者文件到集成终端窗口中，文件夹或文件的完整路径名会自动复制到终端里。

<img src="https://static001.geekbang.org/resource/image/ba/a7/bac1f0bfbf2b446c164bcc65e4877ea7.gif" alt="">

第二种，在集成终端中运行当前编辑窗口中打开的文件，也可以在编辑窗口中选中一段文本直接在终端运行。方法是运行命令Terminal: Run Active File in Active Terminal，或者是Terminal: Run Selected Text in Active Terminal。

<img src="https://static001.geekbang.org/resource/image/ff/26/ff8f05f311750112deb548266f452d26.gif" alt="">

第三种，集成终端中的命令输出，如果包含文件名，可以用Cmd+鼠标点击，直接在编辑器中打开这个文件。这个操作我经常用，比如搜索文件时，因为我对fd和rg命令很熟悉，所以有时会直接使用fd + rg的组合，而不是用VS Code的原生文件查找功能了。

<img src="https://static001.geekbang.org/resource/image/bd/67/bdf559052af53c995433c487ed0f2767.gif" alt="">

第四种，可以安装针对VS Code的命令行工具。比如，使用了[oh-my-zsh中的vscode插件](https://github.com/robbyrussell/oh-my-zsh/tree/master/plugins/vscode)后，我们可以在集成终端中使用vscr在当前VS Code中打开文件，用vscd进行文件比较等工作，实现了终端和IDE的更紧密集成。

<img src="https://static001.geekbang.org/resource/image/0d/25/0da8a4c117fe709fc7462134a0171725.gif" alt="">

**其次，是IDE和代码仓的集成，包括Gist、GitHub Pull、Git Graph。**

在IDE中进行代码仓的操作集成，我个人是比较谨慎的态度。我会比较多地使用命令行的Git，因为我认为目前还没有GUI工具能暴露Git的全面功能。在IDE中，我只选择最适合在图形界面中使用的一些场景。

比如，在VS Code中，我只使用了3个插件：

- [Gist](https://marketplace.visualstudio.com/items?itemName=kenhowardpdx.vscode-gist)，方便在编辑器中直接上传文件。
- [GitHub Pull Request](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github)，方便管理GitHub上的PR处理，可以直接在VSCode里查看和进行讨论，非常方便。
- [Git Graph](https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph)，用于查看历史。它可以显示提交历史的图结构，点击提交可以直接查看文件，比命令行工具更快捷。

如果你想使用这几个插件，可以参考它们的官方文档。

以上就是工具集成方面的内容，接下来我们来看看如何提高工具一致性。

## 提高工具一致性

提高工具的一致性，主要方法是减少常用的工作入口，并对这些入口进行优化。

工具太多容易混乱，但如果能控制入口数量，也就是说进行工作时，只从几个有限的入口开始操作，同时对这些入口进行优化，就能提升工具使用体验的一致性，降低使用工具带来的负担。

要减少并优化常用的工作入口，那我们首先需要明确**经常使用的、必要的工作入口**。

在我看来， 命令行、IDE、桌面快捷启动工具（Launcher）和浏览器这4个工作入口非常必要，是值得重视并优化的。

### 1. 命令行

我比较喜欢命令行的一个重要原因是，它是一系列工具的共同入口。绝大部分命令行的命令都遵循一定的规律，又有统一的man命令查看手册，很容易找到使用方法。

使用命令行减少工作入口还有一个办法，是使用统一的客户端工具进行多种操作。比如，Git命令带有很多子命令（比如，log、diff、show等），从而一个Git命令行工具可以完成很多工作。

在Facebook，很多工作都通过Arcanist（Phabricator 的命令行工具）的各种子命令来完成。这样就实现了命令使用的一致性，降低了学习成本。

在日常工作中，你也可以尝试开发一个命令行工具，对常用工作进行封装。之前我在Stand的时候，就使用Node.js的[c](https://www.npmjs.com/package/commander)[ommander](https://www.npmjs.com/package/commander)模块，对日常研发工作进行了封装。

### 2. IDE

上面已经提过，IDE是一个重要入口，我们应该把较多的开发工作集中到IDE中直接进行。

### 3. 桌面快捷启动工具

桌面快捷启动工具，也是一个非常重要的入口，我们可以通过它进行很多工作。以Mac上非常有名的Alfred来说，你可以用它来启动程序、切换程序、搜索文件、进行网页搜索、管理系统剪贴板历史、进行计算、运行系统命令（比如，重启机器、锁定机器、关闭特定程序、关闭所有程序等）等操作。

用好这个工具，可以大大提高工具使用的一致性。

### 4. 浏览器

现在，我们会大量使用网站应用，那浏览器是必不可少的入口。你可能会觉得，在浏览器中不就是使用URL吗，还能怎么提高呢？

这里，我就和你分享几种方法吧。

其实，我们比较熟悉的书签功能，就是对在浏览器中使用网页应用的一个优化。因为它可以记录常用的工具，你不用每次都输入。

此外，还有一种你可能不太熟悉但非常有效的办法，就是自己运行一个搜索引擎，并把它设为浏览器的默认搜索。这样，你就可以在你的搜索引擎中定义规则，并在浏览器的地址栏输入符合规则的操作。

比如，你可以在搜索引擎后端中实现对“t <task-id>”的解析，用来打开任务系统中序号为task-id的任务。当你在浏览器地址栏中输入“t 123"的时候，搜索引擎自动解析出任务ID 123，然后跳转到这个任务的URL，比如http://tasktool/123.html。当然这个任务工具可能是Trello，也可能是Jira。</task-id>

在Facebook内部，就有一个这样的公用引擎，支持快捷跳转到公司几乎所有的网站中，同时还可以使用它直接进行各种搜索工作，比如内部网页搜索、wiki搜索等，极大提高了公司各种网站应用使用时的一致性。Facebook开源了这个搜索引擎框架，叫作[Bunny1](https://github.com/ccheever/bunny1/)。

事实上，这种浏览器使用方式相当于把浏览器的地址栏变成了一个命令行入口。因为搜索引擎后端就是一个通用的后端服务，可以实现各种各样的强大功能，所以你可以自己做一个这样的搜索引擎，在浏览器地址栏里充分发挥想象力。

## 小结

只有把工具放到具体的工作环境中，才能发挥它们的真实价值。

今天，我从两个方面与你介绍了怎样把多个工具配置成为高效的工具环境。一是，如何对工具进行集成，减少工具切换时的不顺畅，提高个人的研发效能。二是，如何减少并优化工具的入口，从而提高工具使用体验的一致性，以降低工具使用成本，提高研发效能。

其实，工具环境的配置，是一个需要不断摸索，去寻找最佳平衡点的过程。比如，在IDE中进行集成，应该集成最常用的功能，而不应该尝试把所有的功能都使用插件集成到IDE中。否则，IDE就会变得臃肿不堪，性能下降。

我推荐的办法是采用80/20法则。也就是，集中精力对工作中最常用的20%的操作，进行集成和优化。从我个人的体验来看，这样的平衡点是比较合适的。

总之，我们时刻要牢记，配置环境的目的是，配置出一个强大而灵活的环境，让一切工作流畅，从而提高生产效率，一定要注意投入产出比。

## 思考题

在浏览器作为工作入口的例子中，如果一个任务描述搜索到的任务不止一条时，我们可以怎么处理呢？

感谢你的收听，欢迎你在评论区给我留言分享你的观点，也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见！


