【读书笔记】提高编码效率 —— 《Mac 高效开发指南》

316 阅读25分钟

@[toc] :本文内容摘抄自《Mac 高效开发指南》,本文仅为自己学习用,为尊重原著,全部非文字资源都替换成了原著链接,详情请见原著。

Mac 系统配置

快捷键进阶

光标移动

这里比较推荐使用 Emacs 系的快捷键而不是传统的 Command + ←/→/Delete之类的。因为后者并不通用,比如在终端中就无法使用,而 Emacs 系的快捷键则在几乎所有系统级别的输入框内都通用。

常见的几个行级别操作有:

  1. Ctrl + A:移动到行首
  2. Ctrl + E:移动到行尾
  3. Ctrl + K:删除到行尾
  4. Ctrl + N:移动到下一行
  5. Ctrl + P:移动到上一行

前三个命令在终端中非常常用,能大幅度提高工作效率。后两个命令则在 Vim 系列中很常见,多用于上下切换列表中的选项。

几个比较常见的字母级别操作有:

  1. Ctrl + F:向右(Forward)移动一个字母,等价于方向键 →
  2. Ctrl + B:向左(Backward)移动一个字母,等价于方向键 ←
  3. Ctrl + D:向右删除一个字母,等价于 → + Delete 这个快捷键也很常用
  4. Ctrl + H:向左删除一个字母,等价于 Delete

使用 Option 键

如果觉得按字母操作太过于细粒度,按行操作又太粗粒度,那么 Option 键绝对算得上神器,因为它可以按单词操作。

  1. Option + ←:光标向左移动一个单词
  2. Option + →:光标向右移动一个单词
  3. Option + Delete:删除一个单词

拓展预览程序

对于一些文本文件,按下空格键就可以调用系统的预览程序,快速浏览文件内容。但如果想获得更好的阅读体验,或支持更多类型文件的快速浏览,就需要通过插件来完成。

可以输入以下命令来安装拓展,下面会逐个介绍:

brew cask install qlcolorcode qlstephen qlmarkdown quicklook-json qlimagesize webpquicklook qlvideo provisionql quicklookapk

qlcolorcode

顾名思义,这是一个代码高亮的插件,效果如图所示: 详见原著

qlstephen

这个插件能让我们预览没有后缀的文本文件,效果如图所示: 详见原著

qlmarkdown

这个插件能让我们预览 markdown 文件的渲染效果: 详见原著

quicklook-json

这个插件也很容易理解,提供对 JSON 文件的格式化和高亮支持: 详见原著

betterzip

这个插件我没有安装,因为它需要付费,它可以预览压缩文件内部的内容,还支持自定义各种解压缩时的行为(比如解压缩后自动删除压缩包)。下图是安装前的压缩文件预览图: 安装 BetterZip 前系统默认样式 详见原著 安装 BetterZip 后的预览效果 详见原著

qlimagesize

这个拓展可以展示图片的分辨率和大小: qlimagesize 效果 详见原著

webpquicklook

这个拓展可以让我们预览 WebP 格式的图片: 预览 WebP 格式图片 详见原著

qlvideo

预览更多格式的视频文件: qlvideo 预览视频 详见原著

provisionql

这个差距可以预览 .app 或者 .ipa 后缀的程序: 预览 ipa 或 app 文件 详见原著

quicklookapk

预览安卓的 .apk文件: 预览 apk 文件 详见原著

以上就是和程序员相关的常用插件,还有一些设计师可能用到的插件,可以访问这个网址自行查看。

用好 F1-F12

默认情况下,键盘最上面一排的 F1- F12 都有各自的作用,从图标上就能看出。比如 F1 和 F2 用来调节屏幕亮度,F5 和 F6 调节键盘灯亮度灯。此时他们可以被理解为特殊按键,更偏向与娱乐,而不是普通的 F1 - F12。

对于工作重心在编程的码农来说,这些特殊按键有些浪费,而很多 IDE 提供的快捷键都需要 F1 - F12,尤其在断点调试时格外有用,以 Xcode 为例说明下:

  1. F6:执行下一行代码
  2. F7:跳到代码内部执行,比如当前停在一个函数上,F6 会直接执行这个函数,F7 会跳到函数内部的第一行去执行
  3. F8:跳出当前代码块,和 F7 的作用恰好相反 这里的 F6 是标准按键,需要我们同时按下键盘左下角的 fn 键和顶部的 F6 键才能触发,效率极其低下。然而,作为程序员,我们并不太需要那些花哨的娱乐功能,因此我建议在设置中把 F1 - F12 改为标准按键,如下图所示:

将 F1、F2 等设置为标准功能按键或者更高效的做法是使用命令行:

defaults write -globalDomain com.apple.keyboard.fnState -int 1

完全键盘控制

很多操作都会弹出系统的对话框,要求我们确认或者取消

如果没有开启完全键盘控制,我们只能按回车键确认,或者移动鼠标选择取消。如果开启了完全键盘控制,只要按下空格键,就相当于选中蓝色边框的按钮,也就是这里的取消键。按下 Tab 键可以在多个按钮之间切换。

完全键盘控制可以在系统的设置界面开启,也可以直接通过命令行开启:

defaults write NSGlobalDomain AppleKeyboardUIMode -int 3

邮件规则

无论是公司邮箱还是个人邮箱,经常会收到具备某些固定特征的邮件。比如银行信用卡的邮件,某个网站订阅后的邮件,或者公司内特定部门、发件人的邮件等等。对于这类具备固定特征的邮件,我们可以利用系统自带的邮件应用,给他们添加分类、过滤规则。

以个人邮箱为例,假设我想整理所有谷歌账号相关的通知邮件,首先在邮箱列表找到它,然后点击右侧的加号,添加某个子文件夹: 详见原著 然后输入子文件夹的名字: 详见原著 接下来在邮件列表中选中来自谷歌的账号提醒邮件,按下 Command + , 打开邮件设置,找到最后一列的规则,选择添加规则。注意:也可以不选中邮件直接添加规则,这样的缺点是不能识别邮件的特征,需要自己手动填写。

点击添加规则: 详见原著

首先填写规则描述,这个只要能让自己以后看得懂即可。可以给规则添加多个条件,并且指定同时满足或满足任一条件。

每个条件可以在邮件的发件人、主题、收件人等各种特征之间选择,如果当初选中了邮件再打开规则列表,就会自动那条邮件的特征,否则只有自行填写了。

最后的操作可以有多个,比如先标记为已读/星标再移动到刚刚创建的子文件夹下,或者对于公司里特别烦人的全员邮件,可以直接删除。

最后点击确定,然后应用到所有邮件即可。

iTerm2

iTerm2 是一个用来取代系统终端的命令行工具,功能比原生的终端程序强大很多,可以一行命令安装:

brew cask install iterm2

光标移动

上一章 系统快捷键进阶 一节中曾经介绍过光标移动的快捷键,其中以 Ctrl 键开头的快捷键依然可用,比如移动到行首、行尾,向左/右移动或删除字符等等。

但在 iTerm2 中无法使用 Option 键进行单词跳转,因此需要自行配置快捷键,我选择用 Command + ← 来表示光标向左移动一个单词。对应的,向右移动一个单词就是 Command + →。打开设置,添加这两个快捷键: 详见原著

选择文本

iTerm2 中选择文本有三种方式,分别是:

  1. 双击:选中单词
  2. 三击:选中整行
  3. 四击:智能选择

智能选择是一个比较有意思的功能,完整的介绍可以参考官方文档。对于我来说,以下几种情况下用智能选择是非常棒的:

  1. 代码片段,比如智能选择会自动选中 namespace::identifier 以及 foo.bar.baz 这种语法
  2. 双引号内的字符,比如 "this is a quoted string"
  3. 电子邮件,智能选择会选中整个邮件地址
  4. 完整的 URL,智能选择会选中 scheme

另外,iTerm2 默认选中即复制,所以不用额外的 Command + C 这一步了。

Command 键

Command 主要用来打开文件或者 URL。对于输出到终端的文件名或者 URL,我们可以按住 Command 键点击它,就可以直接打开了。

此外,按下 Command + Option 还可以选中矩形范围内的文本。

Tab 和 Pane

按下 Command + T 可以新建一个 Tab,每个标签页的后面都会标记一个序号,通过 Command + 序号 就可以在多个页面之间切换了,或者用 Command + [Command + ] 来切换到左侧或者右侧的标签页。

同一个 Tab 内,还可以切分出多个 Pane,有两种切分方式:

  1. Command + D:水平切分,切分出一左一右两个 Pane
  2. Command + Shift + D:垂直切分,切分出一上一下两个 Pane

通过 Command + Ctrl + 方向键 可以调整每个 Pane 的大小,通过 Command + Option + 方向键 可以切换 Pane。

快速编辑

有时候,如果上一条命令出现了某个小错误,其实不必重新打一遍,可以使用 \^old\^new 的写法进行替换: 详见原著

对于特别长的命令,可以使用 zsh 提供的快捷键 Ctrl-x + Ctrl-e 进入 vim 编辑: 详见原著

Alfred

Alfred 绝对是让 Mac 更加好用的神器之一,如果 Mac 上只能安装一款三方软件,那绝对是 Alfred。我把 Alfred 拔高到如此地位绝非是为了捧杀它,而是确实实至名归,本文主要会介绍 Alfred 原生的用法。

除了 Alfred 软件自带的功能外,它还允许用户自己开发 workflow 并分享出来,正是这些 workflow,彻底奠定了 Alfred 的神器地位,下一章将会详细介绍它们的详细使用方式。

Alfred 简介

打开程序,首页中可以编辑 Alfred 的启动快捷键,这里我把它设置为 双击 Command 键,最好用的 App 当让要配备最好按的快捷键。 详见原著

Alfred 使用的是免费 App + 付费高级功能的模式,本文所介绍的所有内容都是 Alfred 免费提供的。

文件/程序搜索

文件搜索是 Alfred 最基础的功能,类似于系统的 Spotlight,直接双击 Command 打开 Alfred 的窗口,然后输入文件名即可。 详见原著

这也是为什么我会设置 Dock 栏隐藏的原因,毕竟有了 Alfred,谁会去 Dock 里面找 App 呢。

关于文件的搜索,我们还可以配置搜索的范围,搜索哪些类型的文件等等,读者可以在 Features -> Default Results 中配置。

网络搜索

如果我们想通过 Google 搜索东西,常规步骤是:

  1. 打开 Chrome
  2. 在地址栏输入 google.com
  3. 打开谷歌主页,输入搜索内容

然而有了 Alfred,搜索步骤变成了:

  1. 唤起 Alfred(显然比打开 Chrome 再进入 google.com 快得多)
  2. 输入搜索内容,回车

如图所示,先输入字母 g,大概率你会看到这个场景: 详见原著

这说明 Alfred 目前认为,你最有可能的行为是搜索 Chrome 这个应用并打开,所以把它排在第一位,第二个选项才是使用 Google 搜索。

没关系,我们 按照提示,按下 Command + 2,效果等同于点击第二个选项。Alfred 很聪明,会智能记忆,只要多重复几次,使用 Google 搜索就变成默认选项了。 详见原著

此时按下 Tab 键或者回车键都可以快速补全命令: 详见原著

输入想要搜索的内容按下回车,Alfred 就会自动帮我们打开浏览器,并且拼接好地址,直接跳转过去。

PS:如果你觉得 Google 搜索非常高频,不想每次把 g 补全为 google,可以在 Features -> Web Search 中修改 Google 搜索的 keyword。在 Web Search 的配置中,你还会看到除了 Google 搜索外,更多的搜索方式,比如搜索 Wikipedia、YouTube 和 Gmail 等等。

计算器

有了 Alfred,我们就不需要找到系统的计算器并打开了,可以直接在 Alfred 中做基本的运算: 详见原著

剪贴板历史

考虑下面几种场景,你的做法是什么?

  1. 不知道现在剪贴板中的内容是什么,需要先粘贴出来,如果不满意再删除?
  2. 想要拿到上上次复制的内容,但是剪贴板里面已经被覆盖了
  3. 上周复制过一段话,依稀只记得某些关键字,想要再把内容找出来

有了 Alfred 的剪贴板增强,以上场景都不是问题,首先要在 Features -> Clipboard 中打开剪贴板历史: 详见原著

这里我把它们都设置为存储三个月,同时把打开剪贴板的快捷键设置为 Command + Shift + C,按下快捷键我们会看到: 详见原著

所有的复制历史,包括图片的预览都一目了然,我们还可以搜索剪贴板历史: 详见原著

同样通过 Command + 数字键,就可以把它粘贴出来。同时被粘贴出来的内容会移动到剪贴板历史的最前方。

文本片段

有一些文本是需要反复输入的,比如你的邮箱、QQ 号、地址、手机号码、某个网址等信息,亦或者聊天常用语句、表情等等,这些都可以通过 Alfred 来管理,节省输入时间。打开 Features ->; Snippets详见原著

首先勾选右上角的 Automatically expand,它表示只要你输入关键字,就会自动展开成文本内容。 详见原著

建议根据文本的使用场景,建立多个 Collection,比如开发相关的,个人信息相关的,聊天相关的等。

Vim

初识 Vim

我使用了 neovim 来替代 vim,主要区别在于 neovim 更加先进,并且支持插件的异步加载。在使用上和 Vim 几乎是一致的

安装 NeoVim 需要以下几步:

brew install nvim
pip3 install --trusted-host pypi.python.org neovim jedi ipython
pip3 install --user --upgrade --trusted-host pypi.python.org PyYAML

# 下载配置
git clone https://github.com/bestswifter/vim-config.git ~/.config/nvim
ln -s ~/.config/nvim ~/.vim

用 nvim 命令打开文件后可以看到如下效果: 详见原著

Vim 基本操作

Vim 分为三种模式:

  1. 输入模式:这就很普通的编辑器一样,光标在某个位置上闪烁,表示我们可以输入内容了。
  2. 普通模式:在输入模式下按下 Esc键可以退出输入模式,进入到普通模式。
  3. 命令模式:执行搜索、替换等命令或调用插件。

普通模式是 Vim 的一大特色,能够进行很多操作,比如:

  1. 快速移动光标
  2. 快速替换、删除某些内容
  3. 进入输入模式或命令模式

使用 nvim 打开文件后,默认进入普通模式,如果我们要编辑文本,可以按下 i 进入编辑模式,编辑完后再按下 Esc 键可以退出输入模式,进入到普通模式。

如果想退出 nvim,则需要借助命令模式。按下 : 并输入以下内容之一:

  1. q:退出
  2. q!:如果对文件作了修改,无法用 q 退出,此时 q! 表示丢弃修改并退出
  3. x:保存并退出

因为每次按下冒号还挺麻烦,所以我对我的 Vim 做了配置,无需进入命令模式,直接在普通模式就可以退出:

  1. q:退出
  2. Ctrl-q:丢弃修改并退出
  3. Ctrl-x:保存并退出 这种效果可以使用 Vim 的 map 配置来实现:
nnoremap <silent><buffer> q  :pclose!<CR>:quit<CR>
nnoremap <C-x> :x<CR>

// nnoremap 表示普通模式下的 map,等价于先输入冒号再输入 quit
// inoremap 表示输入模式下的 map,因此输入模式下也可以用 C-q 快捷键
nnoremap <silent> <C-q> :<C-u>:quit!<CR>
inoremap <silent> <C-q> <Esc>:<C-u>:quit!<CR>

Vim 光标移动

上一节中我们介绍了 Vim 的基本操作,靠着 i:x等指令,配合上方向键,基本上能在 Vim 的世界里幸存下来了。勉强编辑一下服务器上的文件不是问题。

如果你的打字手势标准的话,你会发现方向键其实挺难触摸到,至少会破坏右手的姿势。因此在 Vim 中不鼓励使用方向键,可以用 hjkl 来代替:

  1. h:向左移动一个字符
  2. l:向右移动一个字符
  3. j:向下移动一行
  4. k:向上移动一行

重复移动

如果要向上移动 5 行,或者向右移动 10 个字符,我们是不是要重复按下很多次呢?显然只有方向键才要这么做,Vim 中可以用数字来表示重复:

  1. 10j: 向下移动 10 行
  2. 5l:向右移动五个字符

这个操作虽然简单,但是引出了两个很有意思的话题。首先,Vim 中的行号应该配置为相对行号 + 绝对行号: 详见原著

图中左侧高亮显示的 22 是当前的绝对行号,上下都是相对偏移量。这样的好处在于既方便知道当前的位置,又方便通过相对行号进行跳转。

另一个话题则是贯穿 Vim 始终的核心思想:就像英语、中文有语法一样,Vim 也有语法。

Vim 的操作可以由若干个部分组合而成,每个部分又有多个可选值(比如光标移动就分上下左右)。Vim 的入门难度高,是因为每个部分都要学习,一下子需要掌握很多新知识点。但 Vim 后期的学习成本很低,因为无非就是各种组合而已。

还是以光标移动为例,5j重复次数动作组成,后面我们会介绍更多的动作,而且它们的前面都可以加上数字表示重复。

单词移动

前面我们讲过利用 Option 键进行单词间移动,得益于普通模式和语法,Vim 支持更强大的单词间移动:

  1. w 移动光标到下一个单词的词首(word)
  2. b 移动光标到上一个单词的词首(begin)
  3. e 移动光标到下一个单词的结尾(end)
  4. ge 移动光标到上一个单词的结尾(不知道怎么解释了。。。)

这里的单词是有严格约定的,而且不是那么显然,所以个人感觉这几个键并不是特别好用(往往你以为的跳转地址和真实跳转地址并不一致)。

不过如果你把空格当做单词的分隔符,倒是有比较简单的跳转方式,前三个命令都有大写格式:

  1. W 移动光标到下一个单词的词首(word)
  2. B 移动光标到上一个单词的词首(begin)
  3. E 移动光标到下一个单词的结尾(end)

这里的单词特指以空格分割。

同样的,这些移动也支持重复任意多次

在后面的插件一节中,我会介绍如何用 EasyMotion 来让光标移动变得简单。

行和屏幕移动

在行内,^ 表示移动到行首,它会自动忽视掉行首的空格,大部分情况下这是符合预期的,尤其是在代码缩进的情况下。如果想回到行首的空格上,可以用 0$ 表示移动到行尾。

Vim 的翻页快捷键是:

  1. Ctrl + f:向后翻一页(forward)
  2. Ctrl + b:向前翻一页(backward)
  3. Ctrl + d:向后翻半页(down)
  4. Ctrl + u:向前翻半页(up)

很巧的是,前两个命令和系统输入框内的,向左向右移动一个字符的快捷键是一样的。后两个命令(u 和 d)则被 Vimium 借鉴,用于浏览器的翻页。

还有几个快捷键可以用于调整页面布局:

  1. zz 让当前行居中显示,方便浏览上下文
  2. zt 让当前行显示在最上方,方便浏览后面的内容(t 表示 top)
  3. zb 让当前行显示在最下方,方便浏览前面的内容(b 表示 bottom)

高级移动

代码中经常遇到括号(包括([{,也就是大中小三中括号),如果当前光标停留在括号上,按下 % 即可匹配到对应的括号上。

除此以外,*# 分别表示匹配当前光标下的单词,并移动到下一个单词和上一个单词

文本编辑

阅读完前两章,相信读者以及能够在 Vim 中自如的移动光标了,不过我们的编辑方式还比较简陋,只能进入编辑模式,像往常一样逐个字符的输入和删除,本节会介绍一些更高级的操作。

基础文本编辑

在普通模式下,按下 o 会快速进入输入模式,并且在下一行进行编辑。如果按下大写的 O 会在上一行编辑: 详见原著

如果想在当前光标位置开始编辑,可以按下 ai,前者表示在光标所在字符的右侧开始编辑,后者表示在左侧开始编辑。

我们还可以用 I 前往行头开始编辑,或者用 A 跳转到行尾编辑。

按下 dd 可以快速删除一行,此时被删除的那一行会保存在 Vim 的剪贴板中。这一点类似于系统的 Ctrl + X 剪切,我们可以通过配置 Vim 来选择是否要与系统共享剪贴板。

普通模式下,按下 p 可以把 Vim 剪贴板中的内容再拷贝出来,注意这里总是会拷贝到下一行或者光标的右侧。因此如果想交换当前行和下一行,可以输入:ddp,这是因为当前被删除的行会复制到下一行的下面。

如果只想复制,不想删除,可以把命令 p 换成 y(表示:yank)。如果想删除后进入编辑模式,可以把 p 换成 c,比如 cc 表示删除当前行并且从行首开始编辑。

注意这里的 p、c、y、d 都是动作,和前面说的 j、k、l、h、w、b、e 一样,都支持用数字来重复,比如 2dd 表示删除两行。

撤销与重做

如果想撤销上一次修改,可以用 u,类似于系统的 Command + z 快捷键。

如果想重做,可以用 Ctrl + R,类似于系统的 Command + Shift + z 快捷键。

文本对象

Vim 的文本对象能极大幅度的提高编辑效率,也是 Vim 进阶之路上必须要掌握的概念。

对于上文介绍的 c、d、y 这些动作来说,作为单独的动作用处并不大,无非就是连续按两下对当前行做一些操作,而文本对象则扮演了语法中名词的概念,提供了更细粒度的操作。比如以下几种操作:

  1. diw: 删除一个单词(word)
  2. ca":删除双引号内的文本并编辑(包含双引号自身)
  3. yi":复制双引号内的文本(不包含双引号自身)
  4. dtx:向右删除,直到遇到遇到字符 x(不包含 x)

以上三个命令都属于同一大类操作,它们普遍遵循以下模式:

命令 = 动作 + 数量 + 描述词 + 文本对象

这类命令通常由三个到字母组成:

  1. 第一个字母是动词,包括上文介绍的 c、y、d 等,表示你这个命令的主要目的。
  2. 第二个字母是可选的,表示重复几次
  3. 第三个字母是对范围的描述,比如 a 表示包含边界,i 不包含,t 表示向右查找,T 向左。
  4. 第四个字母是文本对象,可以是内容的字面量描述,比如 "、'、<、`、{、( 等等,也可以是固定单词

这里详细介绍下文本对象,除了字面量外,以下单词有特定的含义:

  1. p:表示段落
  2. s:表示句子
  3. w:表示单词
  4. t:特指标签,一定要具备 <xx>abc</xx> 的结构,此时的 t 代表 abc

对于字面量的文本对象,除了 "、' 和 这三个引号外,另外几个括号都可以用 b 来表示(block),这样就不用区分大中小和尖括号了。

搜索与替换

使用 / 进行搜索,我的搜索默认是忽略大小写的,使用 /pattern\C 可以强制匹配大小写。

使用 :范围s/oldpattern/newpattern/替换模式 可以进行替换。

如果我们已经进行过搜索,那么 oldpattern 可以省略不写,默认就是上一次的搜索内容。

最常用的范围是 begin,end,其中 begin 是开始行号,end 是结束行号,表示在第 begin 行到第 end 行之间进行替换,比如::1,10s/hello/hallo/g 表示把第 1 - 10 行中所有的 hello 换成 hallo

范围还有一些简单的表示法:

  1. .,10 表示当前行到第 10 行,点表示当前行
  2. .,$ 表示当前行到文件末尾,美刀符号表示文件结尾
  3. % 表示 1,$,也就是整个文件,比如全文替换通常写成 :%s/old/new/g
  4. .,+2 表示当前行和接下来的两行

上文反复提到的字母 g 其实是一种替换模式,表示替换这个范围内所有出现的 oldpattern,如果不写则只替换第一次出现。常用的模式还有 c,表示替换需要确认(Confirm),i 表示大小写不敏感,I 表示大小写敏感。

如果需要替换光标停留的单词,并不需要搜索,或者手动输入它。直接使用 <Leader + s> 即可。接下来只需要输入替换后的内容和替换模式。这种替换是全局替换。

大小写切换

以下是几个常用的,切换大小写的命令

  1. guu 当前行全部小写,gUU 当前行全部大写
  2. ~ 当前光标下的字符切换大小写,3~ 当前光标下后面三个字符切换大小写
  3. guiw 当前单词全部小写,gUiw 当前单词全部大写
  4. g~iw 当前单词每个字符大小写交换

列操作

如果有多行文本要做相同的操作,可以借助 Vim 中列操作的概念来快速完成,举一个实际实际的例子,假设有以下这段代码:

int a;
int b;
int c;
// ...
int z;

有一天我们发现 int 不是很准确,要改成:

uint8_t a;
uint8_t b;
uint8_t c;
// ...
uint8_t z;

难道还要手动复制粘贴不成?请看列操作大法:

  1. 首先按下 Ctrl + V 进入列操作模式
  2. 然后用 j、k 进行跳转,表示你要操作多少行
  3. 这时你对每一行的操作都会应用到所有行上,比如输入 ec,删除单词进入编辑模式
  4. 输入 uint_8,按下 Esc 退出编辑,Vim 会自动应用到所有行上。 详见原著

列操作的本质是多行同时操作,如果每一行的操作类似,但都有细微差别,就需要引入 录制、回放 的概念了。此时可以用 Vim 中的宏。

一个典型的场景是,我们在写 Markdown:

1. 第一条
2. 第二条
3. 第三题
...
10. 第十条

写着写着突然发现,第一条和第二条中间还要加一条,左边的序号就不好处理了,因为我们要把原来的序号 2 到 10 分别加一。

在 Vim 中,我们“只”需要输入 qajC-aq6@a,相信你一定很想打我,这特么一点也不简单啊。别急,拆开来看:

qaq 表示开始录制宏,a 表示录制内容存储在寄存器 a 中)
j(向下一行)
Ctrl + a(数字加一)
q(再按一次 q 表示结束录制)
6@a(把存储在寄存器 a 中的宏执行 6 次)

是不是感觉 So Easy(录像中我用的是大写 Q,因为小写 q 被映射了): 详见原著

Vim 插件

在本章的开头,我就提到目前的主力 IDE 是 VSCode,其实和 Atom/Sublime 这类 IDE 一样,它也是模拟了 Vim 的按键,并不支持 .vimrc 配置文件。不过 VSCode 的强大之处在于,它支持了部分 Vim 的插件。

我想,不需要我做过多解释,读者也能明白这几个被支持的插件的重要性了,必然都是 Life-Changing 系列的。下面就逐个介绍一下:

Easymotion

从名字就能看出,这个插件的目的是快速光标跳转。它支持按行跳转,按字符/字符串跳转和按单词跳转等。跳转时还可以选择方向为向前或者向后, 不过这就增加了一倍的复杂度,作为一个懒人,我一般选择全屏跳转。

我配置的快捷键有:

  1. ;s:按字符串跳转(跳转到开头)
  2. ;w:按单词的开头跳转
  3. ;e:按单词的结尾跳转
  4. ;c:按字符串跳转
  5. ;l:按行首跳转(忽略空格) 详见原著

完整的快捷键列表可以查看文档

Surround

上一节中介绍过文本对象(Text Object)的概念,它主要用来描述括号和引号中间的文本,但考虑一下变换:

"hello" -> 'hello'

如果想把双引号换成单引号,就远远比改变 hello 这个单词难了。而 Surround 这个插件的目的,就是为了方便对引号和括号做修改。

它支持以下几种用法:

  1. y s motion + <char>:给指定范围添加 surround 字符,比如:ysaw" 表示用双引号包裹单词。
  2. d s <char>:删除周围的 surround,比如 ds" 表示删除两侧的双引号
  3. c s <old-char> <new-char>:更换周围的 surround,比如 cs"' 表示把周围的双引号换成单引号。 详见原著

Comment

大多数 IDE 都支持 Command + / 给当前行添加注释,而且会根据语言自动选择注释方式。

在 Vim 中,给当前行添加/取消注释的方式是用 gcc,虽然多了一个按键,但按起来更容易了,而且支持多行注释。这里前两个字符 gc 表示按行注释,最后一个 c 表示当前行。如果想注释当前行和下面一行,可以输入 gcj

美中不足的是,有些语言(如 HTML)中 VSCode 会用 /* ... */ 的写法进行多行注释,这样如果想取消注释,就只能去注释开始或结束的那一行把注释全部取消,而不能只单独取消中间某一行的注释。

输入 gC 可以注释某一块代码,后面接一个文本对象,比如 gCi( 表示注释括号内的东西(不包含括号)。不过个人认为注释小括号的需求几乎不存在,但下面这个场景还是有的,考虑这段代码:

if (1 > 0) {
    printf("Hello");
    printf("wrold");
}

有时候我们只想注释掉那两行 printf,写点别的逻辑,有时候会注释掉整个 if。问题是,怎么用文本对象来表示呢?下一个插件可以让我们用文本对象的方式来表示某一块缩进

Indent

其实很简单,一般就两个文本对象:

  1. ii:表示某一块缩进,不包含上一层,比如上面例子中的两个 printf
  2. aI:表示某一块缩进,包含上一层,比如上面例子中整个的 if 语句

其实还有一个不太常用的,ai 表示某一块缩进,和它的上面一行,也就是上面例子中的前三行,不包括第四行的大括号。这在 Python 中非常常用,因为它没有大括号,完全靠缩进来区分代码块。

因此,注释掉两行 printf 的语法是 gCii,注释掉整个 if 语句的方法是 gCaI详见原著

Sneak

sneak 是和 EasyMotion 差不多的插件,主打快速跳转的功能。用法是 s<char><char>,即按下 s 键后输入两个字符,可以跳转到下一个,这俩字符连续出现的位置。

然而相比原生的插件,VSCode 似乎有一个很严重的问题:不支持搜索高亮,这就导致我根本无法肉眼看出在当前位置和我的目标位置之间,隔了多少个 <char><char>,导致无法用重复来跳转,也不支持标签跳转。更何况这个功能以及由 EasyMotion 实现了(快捷键 ;s),所以我就不再研究了。

VSCode 介绍

基本设置

  1. 在 VSCode 中,按下 Command + , 可以编辑配置,配置分为全局配置和项目配置,项目配置的优先级高于全局配置。
  2. 按下 Command + k + Command + s 键可以管理快捷键。
  3. 按下 Command + p 可以在项目中根据名称搜索并打开文件。
  4. 按下 Command + Shift + p 可以打开命令面板,那些不记得快捷键的命令,都可以在这里找到。

掌握这些就可以初步使用 VSCode 了,下面会列举一些我的配置和插件,更多关于 VSCode 的配置介绍,请参考:vscode-tips-and-tricks

通用插件

  1. Beautify:代码格式化插件,可以将 JS、CSS 和 HTML 代码格式化
  2. Code Outline :支持多种语言的 outline 提取
  3. Code Runner:可以运行多种语言,我配置的快捷键为 Command + R。注意新的文本文件因为没有后缀名,要用快捷键 Command + K + M 手动切换语言类型。
  4. Dash:使用快捷键 Ctrl + h 快速打开 Dash 查找当前单词
  5. MarkdownLint:Markdown 语法检查,帮助你写出标准的 Markdown
  6. Project Manager:帮助你管理项目,在多个项目间切换,快捷键 Option + P(首先要添加进项目列表)
  7. TODO Highlight:高亮显示 TODO
  8. VSCode Icons:根据文件和文件夹名称展示恰当的图标
  9. Bracket Pair Colorizer:给成对的括号配上颜色,方便区分
  10. File Size:在底部 bar 中显示文件大小
  11. Path Intellisense:自动补全文件路径

前端插件

  1. Auto Close Tag:自动补全另一侧的 Tag
  2. Auto Rename Tag:修改一个 Tag 时,另一侧的 Tag 自动修改
  3. Color Highlight:遇到颜色字符串,自动在旁边显示颜色,方便预览
  4. CSS Peek
  5. ESLint:代码格式化,支持实时监测和保存时自动修改
  6. HTML Snippets:HTML 语法片段,方便快速输入
  7. IntelliSense for CSS class:CSS 自动补全
  8. JavaScript code snippets:JS 的语法片段,方便快速输入
  9. Modern JavaScript Snippets:补充了一些更新的语法片段
  10. HTML CSS Support:方便在 HTML 中插入 CSS 代码
  11. SASS:sass 格式文件的代码补全和高亮
  12. Vetur:Vue 开发必备的插件,提供代码补全、高亮等功能
  13. Vue 2 Snippets:Vue 2 的代码片段
  14. Vue peek:快速跳转定义

语法插件

一般用什么语言就装什么插件就行了。比如 Node、Bash、Python、Go、Ruby 等。

集成终端

VSCode 集成了一个终端,可以直接读取 .zshrc 中的配置(也就是说使用起来和 iTerm 几乎是一模一样的)。相关快捷键有:

  1. cmd + \: 水平切割,生成一个新的终端界面
  2. cmd + 数字:当光标停留在终端时,通过数字来选择要聚焦的窗口
  3. cmd + w:当光标停留在终端时,关闭当前终端
  4. cmd + t:当光标停留在终端时,新建一个终端
  5. cmd + l: 在 VSCode 内嵌的终端和代码编辑器中互相切换,再也不用跳出到 iTerm 了
  6. ctrl + `:隐藏/展示 终端

Zsh

默认全局变量

  1. PWD 记录当前所在的目录,通过 echo $PWD 查看

  2. OLDPWD 表示上一次所在的目录,输入减号 - 可以快速跳转到上一次所在的目录

特殊变量

bash/zsh 中还有一些约定好的只读变量,它们的值在脚本的运行过程中动态确定,常见的有:

  1. $0:表示脚本名字,可能是相对路径,当我们执行 bash a/b/c/d.sh 是,$0 的值是 a/b/c/d.sh
  2. $1、$2、……、$10:用来表示参数,$1 表示第一个参数,以此类推
  3. $#:表示参数个数
  4. $?:表示上一个命令的执行结果,0 表示正常结束,非 0 表示出现错误

第 2、3 条规则在函数内部同样适用。

错误处理

本文前面部分内容摘录自阮一峰老师的:Bash 脚本 set 命令教程,主要是文章写得太好了。

必会系统命令

grep

grep 命令很容易学习,它主要有两种使用方式,一种是单独使用,比如搜索某个文件中的内容:

grep 'content' file.txt

或者从标准输入中搜索内容:

echo 'something' | grep 'some'

要想掌握好 grep,重点在于了解它的各种参数。下面是一些常用的参数,如果不记得,后续可以用 man grep 命令来查阅。

grep 在搜索时,默认是大小写敏感的,但如果要搜索 mysql,它可能写做 mysql 也可能写做 MySQL,这就可能存在搜索不到的问题,此时可以用 -i 参数:

echo 'MySQL' | grep -i 'mysql'

如果使用 -n 参数可以打印匹配行的行号,使用 -H 参数可以打印匹配文件的文件名。

默认情况下,如果某个二进制文件中含有搜索的关键词,会显示 Binary file ... matches,使用 -I 选项可以忽略二进制文件,使用 -a 选项可以把二进制文件当做文本文件来处理,从而输出匹配的部分。

默认情况下 grep 会展示匹配的那一行,如果想查看上下文,可以使用 -A-B-C 这三个参数:

  1. -A 3:展示匹配行以及后面的 3 行
  2. -B 3:展示匹配行以及前面的 3 行
  3. -C 3:展示匹配行以及前后的 3 行,等价于 -A 3 -B 3

另外一些常用的选项包括 -v,表示只显示那些不匹配的行,-o 表示只显示匹配的部分,-q 表示不输出内容,通常与 if 连用。

xargs

在前面的章节中我们介绍过,可以通过管道将多个命令串联起来,前提是管道后面的命令要支持从标准输入中读取数据,比如前文的 grep 命令。

然而有些命令并不支持从标准输入中读取,比如这样写是无效的:

echo 'file_name' | rm

此时我们可以借助 xargs 命令:

echo "a" | xargs rm

这条命令的原理是,xargs 会把换行符、空格、制表符、EOF等符号做为分隔符,把输入的内容切分为一个数组,并把数组中每一个元素作为参数,放到后面的命令中执行,用伪代码来写就是:

for arg in read_input; do
    rm arg
done

很常见的一个坑就是,如果文件名带有空格,比如 hello world 就会被 xargs 截断为两个参数,显然不符合预期。不过一般对内容或者文件进行过滤时,我们都会使用 grepfind,这两个命令都有办法配合 xargs

ls | grep 'a' | tr "\n" "\0" | xargs -0 rm

grep 的话会繁琐一些,需要用 tr 命令把换行符转换成特殊字符 \0,再利用 xargs-0 参数,根据文档所述,这个参数会把分隔符指定为 -0,从而避免了文件名中含有空格的影响。

find 也是类似的原理:

find . -print0 | xargs -0 rm

只不过它自带了 -print0选项,写法更简单。

sed

sed 诞生于 1977 年,已经 41 岁了,这么一位叔叔级别的命令至今还活跃在各种 Shell 脚本中,由此可见它是多么重要。

Mac 自带的时 BSD 版本的 sed,因为功能较弱,我不推荐使用,建议使用 gsed,如无特殊说明,下文的介绍都是针对 gsed 的。

brew install coreutils
which gsed
# /usr/local/bin/gsed

sedgrep 的用法类似,都是 sed pattern file 或者 echo 'xx' | sed pattern,也就是说第二个参数可以是文件,也可以从标准输入流中读取。

最标准的用法是进行文本替换(也可以用 tr 命令实现):

echo "a b\nc d"
# a b
# c d
echo "a b\nc d" | gsed 's/a/aa/g'
# aa b
# c d

有时候我们可能不止使用一次 sed,此时可以用 -e 参数把多个命令串联起来:

echo "a b\nc d" | gsed -e 's/a/aa/g' -e 's/b/bb/g'

gsed 中,还可以使用 Shell 里定义的变量:

old=a
new=aa
echo "a b\nc d" | gsed "s/$old/$new/g"

我推荐用 gsed 是因为它有一个 -i 选项,可以对文件进行原地修改:

gsed -i 's/a/aa/g' file

sed 最核心的部分在于这里的 s/a/aa/g,它由若干个斜杠组成(其实也不一定要用斜杠,只要保持一致就行)。这里的 s 表示替换,a 表示待匹配的内容,支持正则,aa 表示替换后的内容,g 表示全部替换,更多的用法有:

  1. 1,3s/a/aa/g:只替换第一到三行中的内容
  2. s/a/aa/1:只替换每行第一个 a
  3. s/a/aa/2g:每行前两个 a 不替换,从第三个开始替换
  4. 2i sss:在第二行前面追加一行,内容为 sss
  5. 1a sss:在第一行后面追加一行,内容为 sss,等价于 2i sss
  6. /a/a sss:遇到有字母 a 的行,就在后面追加一行 sss
  7. 1c sss:把第一行替换为 sss
  8. 2d:删除第二行 这些用法虽然看起来复杂,但是和 vim 一样,每个部分就几种写法,然后自行排列组合即可。

gsed 在默认情况下,会把输入的每一行都输出一遍,它有一个常用的选项是 -n,表示不输出任何一行。通常与 p 命令合用,这个命令可以打印匹配的行,类似于 grep 的效果。

awk

awk 是和 sed 同时代的命令,并称为文本处理两大神器。个人认为 sed 的强大之处在于文本匹配后的处理,而 awk 则更适合文本的结构化处理。

这里以获取 ip 地址的命令来介绍下:

ifconfig | sed -n -e '/127.0.0.1/d' -e '/inet /p' | awk '{print $2}'

这里 awk 的用法其实很简单,就是打印第二列。awk 的核心在于内建的变量:

变量说明
$0当前记录(这个变量中存放着整个行的内容)
$1~$n当前记录的第n个字段,字段间由FS分隔
FS输入字段分隔符 默认是空格或Tab
NF当前记录中的字段个数,就是有多少列
NR已经读出的记录数,就是行号,从1开始,如果有多个文件话,这个值也是不断累加中。
FNR当前记录数,与NR不同的是,这个值会是各个文件自己的行号
RS输入的记录分隔符, 默认为换行符

awk 一个很常见的用法是 -f 参数,可以指定输入字段的分隔符:

echo "a;b;c" | awk -F';' '{print $2}'

其实理论上来说,awksed 还要强大,因为它是一个图灵完备的语言,支持 for 循环等等编程思想。建议感兴趣的读者阅读 AWK 简明教程 了解更多 awk 的使用技巧

终端高效使用指南

Autojump

如果不想每次都输入 cd 再输入 ls,那么 autojump 是必装的神器:

brew install autojump

它会记住每一次 cd 的路径,并且保存在数据中,以后我们可以直接输入 j + 关键字,从而避免频繁的 cd

终端命令自动补全

输入快捷键 Ctrl + E 可以根据当前提示快速补全,快捷键 ; 可以补全并执行 详见原著

终端 Finder 模拟器:r

系统的 Finder 其实并没那么好用,最大的问题在于没法和 Shell 有效的交互,比如复制移动文件、在当前文件夹位置打开终端都很不方便。

作为程序员,我推荐使用 Ranger 来浏览文件目录,它是一个使用 Vim 键位映射的文件管理工具。

使用快捷键 r 来打开 ranger,它的完整定义是:alias r='source ranger',这样做的好处在于当 Ranger 中目录发生变化时,可以改变外部 Shell 的路径。

在 Ranger 中,使用 j/k 来上下移动光标,h/l 来进行目录的前进和后退。

常用的操作有:

  1. zh:切换是否显示系统隐藏文件,按一次打开,再按一次关闭
  2. x:安全删除文件(放入垃圾箱中而不是 rm)
  3. yy:复制,dd:剪贴,pp:粘贴,空格键多选文件
  4. gh:进入用户目录($HOME)
  5. yn:复制文件名,yd 复制文件夹名,yp 复制完整路径名
  6. :j:和 autojump 一样,输入要跳转的地方
  7. Ctrl + f:利用 fzf 搜索文件
  8. f:当前目录内过滤文件名
  9. du:查看当前目录内各文件夹大小
  10. oo:在 Finder 中打开,op 或回车键:使用系统默认的程序打开,oc:使用 VSCode 打开(如果已经有 VSCode 进程,为了加快速度,则使用已存在的)
  11. m:添加书签,um:选择要删除的书签,```:展示书签

fzf:模糊搜索神器

fzf 是一个模糊搜索神器,^t 是特定语义下的补全快捷键,^i是默认快捷键,很少用到:

  1. 输入 kill 然后按下 ^t 键,就会打开 fzf 补全界面,通过输入进程名来获取到 PID
  2. 类似的还有输入 sshexportunsetunlias 等命令
  3. 按下 alt + c 可以列出当前目录下的文件夹,并快速进入
  4. 按下 ^g,会自动补全 autojump 的路径列表
  5. 按下 ^r 进入命令历史模式,此时也会自动打开 fzf 补全界面,自动补全命令
  6. 注意此时的补全并不会自动执行,只会把命令粘贴到命令行中,如果想要按下回车后自动执行,可以用快捷键 ^x^r 来触发 详见原著

fzf 甚至还支持为自定义的命令添加补全,具体做法可以参考:Examples (completion)