我是如何在 2019 年还不使用 GUI 的:终端指南(翻译)

2019-03-16

前言

本文作者为 Lucas Fernandes da Costa,文章源链接为 How I'm still not using GUIs in 2019: A guide to the terminal

这里是我的 dotfiles。欢迎使用!

GUI 是臃肿的软件,我之前已说过。可是,我也不能光对 IDE 耍嘴炮,我得提供一个使用命令行做替代的可行指南。

IDE 是 Integrated Development Environment 的缩写,这可能是个准确的术语,但是在现实中使用 IDE 的时候,会发现不如终端好用。

在本文中,我们一起来看如何把完整的开发环境都放在终端里实现:

  • 如何有效编辑文本
  • 如何配置外观
  • 运行并组合大量程序
  • 动态创建、调整大小、关闭 Tab 和窗口

La Raison D’Être(存在的原因)

在 IDE 中,你只能使用这个 IDE 下专门的插件,而在终端中,你可以使用任何程序。IDE 会把你限制在一个范围,这个范围是 IDE 的创建者认为你想要做什么。

终端则不是这样,它允许你很容易地组合任何程序。由于管道的存在,你可以将一个程序的输出作为另一个程序的输入。

这提供了很大的灵活性,允许你灵活地、有创造性地使用各种工具集,以实现原作者未曾想到的目的。

这也使用 UNIX 哲学进行程序设计的最著名的特性:

Word、Excel、PowerPoint 和其他的微软程序都对彼此内部原理了如指掌,有人会说他们是混杂的。

在 Unix 中,人们设计的程序的操作是通用的,不是专门为彼此,而是针对尚未考虑的程序。

—— Doug McIlroy

GUI 的学习曲线没有命令行陡峭。但是,你对纯文本越熟悉,就越容易输入指令,而不是在界面中寻找选项和按钮。难得地方在于记忆需要输入的指令。

IDE 还倾向于做过多的”魔术“。处于实用性考虑,他们对用于隐藏了基本细节,这回导致在遇到问题时找出真正原因变得更加困难。

这有两个主要原因:

  1. 这层抽象会使人们完全不知道计算机在干什么
  2. 在 IDE 上点一个按钮就会执行大量操作,这使得分解过程和诊断其中有问题的过程变得困难

现在你可能会争辩说,有些 ide 具有带控制台接口的小窗口,这些窗口与终端的输出是相同的,这没错。

但这还不够。如果你仍然不知道如何使用计算机的术语跟计算机打交道,那么你将无法理解 IDE 显示的内容。

习惯使用终端,这意味着你与计算机会用相同的“语言”进行沟通。

终端程序的配置是非常便携的。大多数程序的配置都只需要把一个纯文本文件拷贝到指定地方就完事。

这样,就算换了新机器,几秒至多几分钟时间就能在新机器中部署一个开发环境。

终端程序的配置文件——通常叫 dotfiles——比 ide 的配置文件友好地多。

dotfiles 是人可读的纯文本。与 IDE 相比,它与项目的耦合也比较少。

当你使用 IDE 配置文件描述计算机应该如何构建代码的时候,你将强制团队中每个人使用相同的工具。

换句话说,比如,当你使用 make(终端程序),这就给了人们自由,他们可以从 IDE 中构建,也可以从终端中构建。

所有为熟练使用终端所付出的努力会很快获得回报。

这个回报不是在时间节省上,因为实际中大多数时间是在思考,不是编码,因此几个快捷键省不了几秒钟。

真正带来好处的是,使用终端让你对计算机是怎么工作的有更加深刻的认识,这会让你的工作流变得更可预测、更加灵活。

最后,熟练使用终端是一种“一次学习到处使用”的技能。并不是每台机器都有 VSCode、Sublime 或者 Atom,但是每台机器都有终端。

终端新手包

我使用这些工具将我的终端打造成一个完整的开发环境:

在本文的末尾,你会发现一些有用程序的清单。

neovim

vim 是的文本编辑器选择,它在我的心中和脚踝中都有特殊地位。

译者注:之所以是脚踝,因为这位老哥在脚踝纹了一条 vim 指令:

neovim 是 vim 的另一个版本。它的快捷键跟 vim 一直,特性一样,与 vim 配置完全兼容。

为了避免 vim 在执行语法检查之类的任务时冻结,neovim 允许你异步执行任务。

他还支持多种脚本,有 VimL(这不是我最喜欢的语言),还支持 Lua 脚本。

为了确保使用的是 nvim 而不是 vim,你可以使用下面语句 alias 一下。这个代码你可以放进 .zshrc 或者 .bashrc,或者你的 shell 所使用的配置中:

alias vim="nvim"
alias vimdiff="nvim -d"

学习 Vim

即使很多人害怕 vim,而且在 Stack Overflow 上还有一个老梗——如何退出 vim 是 SO 上最受欢迎的问题。vim 还是更好的,因为它与众不同。

为了学习有效使用 vim,我强烈建议从 vimtutor 开始。

  • 一开始可能会觉得枯燥,但这是正确学习 vim 最有效的方法。

硬着头皮别放弃。

  • 如果你不知道怎么完成一个特定任务,去 duck-duck-go 搜一下。
  • 在开始的几天或者几周内,你会不得不记住哪个键对应哪个功能,但是随着时间的推移,他们会印在你脑子里,形成肌肉记忆。

从一开始就禁用方向键。通过在 ~/.config/nvim/init.vim (之前是 ~/.vimrc`)加入以下代码:

" don't use arrowkeys
noremap <Up> <NOP>
noremap <Down> <NOP>
noremap <Left> <NOP>
noremap <Right> <NOP>

" really, just don't
inoremap <Up>    <NOP>
inoremap <Down>  <NOP>
inoremap <Left>  <NOP>
inoremap <Right> <NOP>

我通常建议已经熟悉 vim 的用户不要在 init.vim 中放任何不理解的东西。

  • 搜索命令的功能会让你对 vim 有更好的理解
  • 这会给你所需的知识来配置你的工具
  • 这样使他们匹配你的编辑风格和工作方式

现在,如果你喜欢毛线,就去 duck-duck-go 上查查 noremap 和 inoremap 这俩命令是干嘛的。

一旦你熟悉文本编辑,开始关注反模式,比如多次重复相同的按键、重复命令,或者进入 inset 模式删除文本。

你应当总是问问自己,是否有更好的方式来做这件事。大多数时候答案都是肯定的。

避免插件,除非你确定要使用它。

  • 大多数时候,vim 已经有你所需要的特性了,这样最好就不要再依赖第三方
  • 用的插件越少,运行速度就越快
  • 避免臃肿让你的 vim 在任何机器上都更容易使用

作为一个初学者,你不知道自己需要什么,因此下面我会加一些你会认为改进工作流的内容。

我通常推荐初学者使用 NERDTreefzf

将来会最终会想整理你的配置,保持整洁。

最后,我能给任何人的 vim 的人的最好建议是采用增量学习法。

  • 试着每天提高一点点,避免在短时间内学习太多的技巧
  • 每天使用你所选择的编辑器(希望是 vim)
  • 每天花点时间来消化一个命令
  • 在余生中使用它

这比快速学习许多技巧并只使用一次更好。

深入发掘,并以适当的速度学习。对此,我强烈推荐 vim Wikia

我目前在使用的插件

要安装和使用插件,你可能需要使用 vim-plug

它是一个极简的,易于安装的,易于使用的插件管理器。

同时它还有很多优秀特性,比如:

这允许你配置额外的安装步骤。

neomake 是 neovim 对我的主要卖点。它允许你异步地运行程序,避免阻塞主线程。

这个特性在运行语法检查和其他 CPU 密集的操作时尤为有用。之前用 vim 的时候,经常会卡住几秒钟。

NERDTree 是一种可视化和管理文件树的简单方式。尽管它的所有特性都有原生的替代方案,但是原生的都不够实用。

我通过下面几行配置 Ctrl+n 来打开 NERDTree 并显示隐藏文件:

" NERDTree on ctrl+n
let NERDTreeShowHidden=1
map <silent> <C-n> :NERDTreeToggle<CR>

" close NERDTree after a file is opened
let g:NERDTreeQuitOnOpen=1

fzf.vim 用于模糊查找,允许你输入部分文件名就能快速查找并打开文件。

安装 fzf 的过程要比其他插件麻烦一些。因为 fzf.vim 是一个 fzf 的封装,你需要使用下面几行来确保二进制文件已被安装:

Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --bin' }
Plug 'junegunn/fzf.vim'

fzf 可以使用其它程序寻找文件,比如 UNIX 的 find,以及 the silver searcher — also known as ag

我喜欢使用 ag,因为我可以配置它来忽略 .gitignore 中的文件,这样不论我使用哪个仓库,都有合理的缺省值:

" make FZF respect gitignore if `ag` is installed
" you will obviously need to install `ag` for this to work
if (executable('ag'))
    let $FZF_DEFAULT_COMMAND = 'ag --hidden --ignore .git -g ""'
endif

最后,我使用 ctrl+p 快捷键绑定 fzf:

nnoremap <C-P> :Files<CR>

editorconfig-vim 插件帮助我快速根据项目适配编辑器设置,只要项目中有 .editorconfig 文件

有了这个插件,我就不用担心 EOF 格式,缩进数量和字符集的问题了。

emmet-vim 能加快写 HTML 的速度。如果你 JSX 写得多——像我一样——需要通过下面配置来正确处理 .jsx 和 .tsx 文件:

" make emmet behave well with JSX in JS and TS files
let g:user_emmet_settings = {
\  'javascript' : {
\      'extends' : 'jsx',
\  },
\  'typescript' : {
\      'extends' : 'tsx',
\  },
\}

我使用 ale 作为语法检查引擎,因为他能与大多数语言的 linter 工作,并且开箱即用。

使用一个支持所有语言的单个工具,避免了我之前配置多个插件以及维护所有配置的痛苦。

有了 ale,如果你项目中有 eslint 和 .eslintrc 文件,它会以正确设置自动运行 eslint,并显示错误。

我还通过配置让语法检查快一些,通过删除行尾和去掉多余空格:

" fix files on save
let g:ale_fix_on_save = 1

" lint after 1000ms after changes are made both on insert mode and normal mode
let g:ale_lint_on_text_changed = 'always'
let g:ale_lint_delay = 1000

" use nice symbols for errors and warnings
let g:ale_sign_error = '✗\ '
let g:ale_sign_warning = '⚠\ '

" fixer configurations
let g:ale_fixers = {
\   '*': ['remove_trailing_lines', 'trim_whitespace'],
\}

插件中最突出的可能是 YouCompleteMe,它提供了像 IDE 一样的基于语义的补全建议。

YouCompleteMe 建议考虑代码的含义(它的语义),而不是只搜索相似的单词(标记)。

YouCompleteMe 插件是一个连接到 YouCompleteMe Server 的 Client,Server 是真正产生建议的地方。

Server 为每种语言运行不同的服务。例如对于 TypeScript,YouCompleteMe 运行微软的 TSServer。

要安装这个插件,你还要安装 YouCompleteMe Server:

" semantic-based completion
Plug 'Valloric/YouCompleteMe', { 'do': './install.py' }

如果上面命令出错,需要确认你的 neovim 是否支持基于 Python 的插件。或者试着运行 ~/.config/nvim/plugged/YouCompleteMe 下的 install.py。

不用说的是,它对强类型语言会更有用一些。我主要测试的语言是 rust 和 TypeScript,都很美妙。

我还使用以下几行代码,将它的建议绑定到 vim 的默认快捷方式和弹出窗口中完成:

" disable auto_triggering ycm suggestions pane and instead
" use semantic completion only on Ctrl+n
let ycm_trigger_key = '<C-n>'
let g:ycm_auto_trigger = 0
let g:ycm_key_invoke_completion = ycm_trigger_key

" this is some arcane magic to allow cycling through the YCM options
" with the same key that opened it.
" See http://vim.wikia.com/wiki/Improve_completion_popup_menu for more info.
let g:ycm_key_list_select_completion = ['<TAB>', '<C-j>']
inoremap <expr> ycm_trigger_key pumvisible() ? "<C-j>" : ycm_trigger_key;

我的语言相关的插件有 rust.vimvim-javascripttypescript-vim,因为这些语言我经常写。

我只在开始使用新语言时配置语言相关的插件。可能你已经注意到了,我是极简主义者。

tmux

tmux 是一个终端的多路复用器。本质上,这个花哨的名字的意思是说,允许你在一个终端窗口中拥有多个选项卡和窗格。

它利用了终端的多功能性,使其更加灵活。

如果你年纪足够大,还记得 IE6 以及不得不使用一堆窗口浏览网页的可怕经历,你可能会记得浏览器引入选项卡时的巨大改进。这是 tmux 提供的相同的改变生活的改进。

在 tmux 术语中,每个选项卡都是一个窗口,一个窗口可以包含一个或多个窗格(分割)。

这些窗口包含在会话中,您可以将其附加或分离到当前 shell。

当分离一个会话时,tmux 服务器将继续运行它,以便您可以在需要时重新连接它。

在后台有会话对于在处理不同任务时保持程序运行非常有用。

在一个窗口中有多个窗格对于编辑文本和需要关注测试非常有用,例如,当您更改文件时,测试就会运行。它对于在一个窗格中运行git命令和在另一个窗格中修复冲突也很方便。

使用 tmux 最显著的优点是,你可以并行运行任意多的程序,查看它们的输出,而不必使用图形界面拖动面板或切换视图。

如果您需要快速切换到不同的任务,但又不想放弃当前工作的上下文,那么使用许多不同的 Tab 也很有用。

tmux 的快捷键绑定包含一个 prefix 和快捷键本身。要触发一个快捷键,按下 prefix 键,之后按下快捷键。

如果你的快捷键是 Ctrl+a,横向切分键是 -。这样先按下 Ctrl+a 在按 -。

你可以将 prefix 理解为告诉终端说我要使用 tmux 快捷键的组合键。

我发现 tmux 默认的快捷键不太好用,因此我做了一些调整。

现在我实用 C-a 作为 prefix,h、j、k、l 作为切换窗格的键,与 vim 相同。- 和 | 用于横向与纵向切分。

# Free the original `Ctrl-b` prefix keybinding.
unbind C-b

# set prefix key to ctrl-a
set -g prefix C-a

# vi keys for switching panes
bind-key h select-pane -L
bind-key j select-pane -D
bind-key k select-pane -U
bind-key l select-pane -R

# Splitting panes.
bind - split-window -v
bind | split-window -h

使用 H、J、K 和 L 来调整窗格大小,所有都是大写:

# shift-movement keys will resize panes
bind J resize-pane -D 5
bind K resize-pane -U 5
bind H resize-pane -L 5
bind L resize-pane -R 5

我经常使用 prefix + z 来出发窗口全屏模式,这样更方便看一些。

过去,我不知道怎么按照 tmux 方式复制文本,因此我做了些改动,得到无缝体验。

首先,我允许 tmux 访问 OSX 的剪贴板:

# Workaround to allow accessing OSX pasteboard
set-option -g default-command "reattach-to-user-namespace -l zsh"

之后,我使用与 vim 相同的快捷键来选择和拷贝文本:

# Vi copypaste
setw -g mode-keys vi
unbind p
bind p paste-buffer
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

有了这些设置,当你在窗格中滚动拷贝文本的时候,只需要 preffix+[ 来开始滚动,h j k l 或者 Ctrl+u 和 Ctrl+d 移动光标,v 或者 shift+v 进行选择,y 来对所选内容进行拷贝。

以下是其他一些自我解释的小改进:

# Start tabs at index 1
# (they usually start at 0, which is too far from where my fingers usually are)
set -g base-index 1

# Make pane numbering consistent with windows
setw -g pane-base-index 1

# Renumber windows when a window is closed
# This guarantees it will be easier for you to switch
# between windows as you keep opening and closing them
set -g renumber-windows on

# Automatically set window title according to the running program
set-window-option -g automatic-rename on
set-option -g set-titles on

tmux 插件

安装管理 tmux 插件需要使用 tpm

安装过程没什么好强调的,跟着 README 操作就行。

我使用的主题是从 nord-tmux 中自己 fork 的。我绝对喜欢他们的配色方案,fork 是因为想自定义状态栏。

一旦确定了要使用的主题,还可以添加新插件并自定义状态栏。

例如,如果你想显示电池电量,需要使用 tmux-battery。在 .tmux.conf 加入如下设置,并使用 prefix + I(I 大写的)来安装:

set -g @plugin 'tmux-plugins/tmux-battery'

引用插件只需要在 status_bar 设置中加入:

set -g status-right 'Battery: #{battery_icon} #{battery_percentage} #{battery_remain} | %a %h-%d %H:%M '

有一本书叫 tao-of-tmux,其中有关于定制状态栏的完整指南

下面是我现在在用的插件列表:

zsh

在 UNIX 中,shell 提供一个基于文本的接口,用于与底层操作系统通信。

你可以把它看作是告诉你的机器它应该做什么的另一种方式,而不是像图形界面经常显示的那样,你键入命令,然后点击鼠标,让它显示所有杂乱的东西。

通常默认的 shell 是 bash。

zsh 是另一种 shell,提供方便的附加功能。

设置 zsh 作为你的登录 shell,通过下面指令:

chsh -s $(which zsh)

zsh 自动补全比 bash 要好,不仅是文件目录,也包括像 git 这样的程序以及其他

只需要在输入命令的时候按 TAB,他就会帮你补全。

zsh 具有高度扩展性,这也就是为啥 oh-my-zsh 这么流行。oh-my-zsh 是一个框架,管理你的 zsh 设置,轻松添加插件,使用主题和定制其他相关设置。

你可以从 oh-my-zsh 的 wiki 中找到安装方法

zsh 的配置文件名为 .zshrc,位于家目录下。

这个文件在打开新的 shell 实例时会被执行。通过运行它,你可以导出环境变量,运行指令。

在当前的上下文运行脚本被称为 “sourcing it”。

指定默认编辑器,在 .zshrc 中加入:

# Use nvim as the default editor
export EDITOR=nvim

将 alias 分离到一个独立的 .aliases 文件下:

# Load aliases
if [ -f ~/.aliases ]; then
    . ~/.aliases
fi

你可以在 GitHub 上看到我的 .aliases 文件

还可以使用 .zshrc 处理其他方便的任务,比如设置 nvm 来管理节点版本,或者设置 cargo 来处理 rust:

# Add RVM to PATH for scripting. Make sure this is the last PATH variable change.
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

# Add cargo to the PATH
source $HOME/.cargo/env

zsh 插件

我现在的 zsh prompt 是 the spaceship-prompt。它很漂亮,极简,还包括 emoji——千禧一代的一个必备特征。

安装方式很简单,参见这里

我最喜欢的插件是 z。它允许你不用输入目录全称就能跳转过去。

如果你在目录 /Users/Elon/tesla/new-york 跳到了 /london,想在跳回来,只需要输入 z new-york

z 的工作原理是了解您更频繁地访问哪些目录,然后使用这些信息将您带到正确的位置。

我还是用 git 插件,它是一些 git 的有用 alias。其中我最喜欢的是:

  • g → git
  • gcm → git checkout master
  • gp → git push
  • gst → git status
  • grb → git rebase
  • graba → git rebase --abort
  • grbc → git rebase --continue

如果你是在 20 世纪早期氛围,你可以安装 chucknorris 插件,并使用chuck命令来获取曾经有趣的笑话。

# .zshrc plugins section
plugins=(
  z
  git
  chucknorris # ¯\_(ツ)_/¯
)

iTerm2

iTerm2 是一个终端模拟器。不要与 shell 弄混,终端模拟器只不过是一个包含 shell 的窗口。

终端模拟器负责 shell 的显示方式。iTerm2 很好,因为它允许我设置我想使用的字体和颜色。

通过 Ctrl+, 我能配置它的外观和一些有用的选项。

我的 iTerm2 使用 the nord iTerm colours 来匹配我的主题,使用 DejaVu Sans Mono for Powerline 字体来展示文本。

Powerline Fonts 是一个很值得拥有的插件。它包括了许多用于基于文本的布局的字符,比如你可以在我的 tmux 状态栏中看到的填充块和三角形。

我使用 iTerm2 原因是它比 macOS 默认的终端定制性更高。

因为我有 tmux,因此不需要它的窗口管理功能。

其他有用的程序与工具

使用终端作为主交互界面的艺术在于时间与耐心。

最难的地方在于添加新的工具到你的工作流,并习惯他们。

有时候,你甚至不知道你想要做的事情有一个命令行替代方案。

下面是其他有用的工具,可以结合到你的工作流中:

  • curl, 通过 URL 搬运数据,我通常使用它做 HTTP 请求
  • jq, 处理 JSON.
  • sed, 处理文本,我很喜欢它
  • ag, 搜索代码
  • awk, 用于转换和提取数据。我不经常使用它,但它对于编写脚本或有效地组合程序特别有用。
  • cron, 定时任务
  • rsync, 传输文件
  • make, 自动化任务
  • gzip, 处理压缩文件

精通终端用户还需要了解基本管理命令、流、UNIX 权限和 shell 脚本。

其他有用的资源