你好,我是葛俊。今天,我要与你分享的主题是,命令行下的高效工作技巧。

我先和你讲一个有意思的话题吧。命令行工具常常会给人一种黑客的感觉,好莱坞的电影里面常常出现命令行窗口的使用。不知道你听说过没有,很多好莱坞电影在拍摄时使用的其实是一个叫作nmap的工具。这个工具是做安全扫描的,只不过因为它的显示特别花哨,所以被很多电影采用。在nmap官方网站上,还专门列出来了这些电影的名单。

类似这种可以让自己看起来很忙的工具还有很多,比如Genact。下面是一个使用Genact的录屏,当然这里的命令并没有真正运行。这可能是整个专栏中,唯一一个让你看起来效率很高,实际上却是降低效率的工具,但说不定对你有用,你懂的。

讲完这个娱乐性的话题,我们进入正题吧。

为什么要使用命令行?

GUI图形界面的出现是计算机技术的变革,极大方便了用户。但在这么多年后的今天,命令行工具为什么仍然有如此强大的生命力呢?

在我看来,对软件工程师来说,想要高效开发就必须掌握命令行,主要原因包括:

为了演示命令行的强大功能给我们带来的方便,下面是一个在本地查看文件并上传到服务器的流程的录屏。

通过这个案例,你可以看到命令行的以下几个功能:

整个流程全部都是在命令行里完成的,速度非常快,用户体验也非常好。正因为如此,我看到的硅谷特别高效的开发人员,绝大多数都大量使用命令行。那,面对成百上千的命令行工具,我们怎样才能高效地学习和使用呢?

我将高效学习使用命令行的过程,归纳为两大步:

  1. 配置好环境;
  2. 针对自己最常使用命令行的场景,有的放矢地选择工具。

今天,我就与你详细讲述环境配置这个话题。而关于选择工具的话题,我会在下一篇文章中与你详细介绍。总结来讲,环境配置主要包括以下四步:

  1. 选择模拟终端;
  2. 选择Shell;
  3. 具体的Shell配置;
  4. 远程SSH的处理。

这里需要注意的是,在命令行方面,macOS和Linux系统比Windows系统要强大许多,所以我主要以macOS和Linux系统来介绍,而关于Windows的环境配置,我只会捎带提一下。不过,macOS和Linux系统中的工具选择和配置思路,你可以借鉴到Windows系统中。

第一步,选择模拟终端

我将一个好的终端应该具有的特征,归纳为4个:

macOS系统自带的终端不太好用,常见的替代工具有iTerm2、Terminator、Hyper和Upterm。我平时使用的iTerm2,是一个免费软件,功能强大,具备上面提到的4个特征。下面我就以iTerm2为例展开介绍。其他几个工具上也有类似功能,所以你不必担心。

在多终端的场景方面,iTerm2支持多窗口、多标签页,同一窗口中可以进行多次水平和纵向分屏。这些操作以及窗口的跳转都有快捷键支持,你可以很方便地在网络上搜索到。

在管理常用SSH的登录方面,iTerm2使用Profile(用户画像)来控制。比如,下面是一个连接到远程服务器案例的录屏。

可以看到,在我的工作环境里常会用到4个Profile,其中有两个是连接到远端服务器的,包括Mosh Remote Server 1和SSH Remote Server 2。工作时,我使用Cmd+O,然后选择Server 1这个Profile,就可以打开一个新窗口,连接到这个远程服务器上。

每一个Profile都可以定义自己的字体、颜色、shell命令等。比如,Server 1是类生产服务器,我就把背景设置成了棕红色,提醒自己在这个机器上工作时一定要小心。所以在上面的录屏中你可以看到,连接到远端的SSH标签页,它的背景、标签页都是棕红色。另外,下面是如何对Profile颜色进行设置的截屏。

除了这些基础功能外,iTerm2还有很多贴心的设计。比如:

其中第2、3项功能是由一组macOS的集成工具提供的。这个工具集还包括显示图片的命令imgls、imgcat,显示自动补全命令,显示时间、注释,以及在主窗口旁显示额外信息等。这些设计虽然很小,但非常实用。

关于Windows系统,2019年5月微软推出了Windows Terminal,支持多Tab,定制性很强,据说体验很不错。

选择好了终端,环境设置的第二步就是选择Shell。

第二步,选择Shell

在我看来,选择Shell主要有普遍性和易用性这两条原则。

Linux/Unix系统下,Bash最普遍、用户群最广,但是易用性不是很好。常用来替代Bash的工具有ZshFish,它们的易用性都很好。下面是两张图片,用于展示Zsh和Fish在易用性方面的一些功能。

Zsh:

Fish:

我个人觉得Fish比Zsh更方便。事实上,Fish是Friendly Interactive Shell的简称。所以,交互是Fish的强项。可惜的是,Fish不严格遵循POSIX的语法,与Bash的脚本不兼容,而Zsh则兼容,所以我目前主要使用的是Zsh。

选好了模拟终端和Shell之后,便是配置环境的第三步,具体的Shell配置了。

第三步,具体的Shell配置

接下来,我以我自己使用的设置为例,向你介绍Bash、Zsh、Fish的具体配置吧。这里,主要包括命令行提示符的配置和其他配置两个方面。

之所以把命令行提示符单独提出来,是因为它一直展现在界面上,能提供很有用的价值,对命令行高效工作至关重要。下面是一张图片,展示了Bash、Zsh和Fish的命令行提示符。

这个窗口分为三部分,最上面是Bash,中间是Zsh,最下面是Fish,都配置了文件路径、Git信息和时间戳等信息。接下来,我带你一起看看这3个工具应该如何配置吧。

Bash比较麻烦,配置文件包括定义颜色和命令行提示符的两部分:

## 文件 $HOME/.bash/term_colors,定义颜色
# Basic aliases for bash terminal colors
N="\[\033[0m\]"    # unsets color to term's fg color

# regular colors
K="\[\033[0;30m\]"    # black
R="\[\033[0;31m\]"    # red
G="\[\033[0;32m\]"    # green
Y="\[\033[0;33m\]"    # yellow
B="\[\033[0;34m\]"    # blue
M="\[\033[0;35m\]"    # magenta
C="\[\033[0;36m\]"    # cyan
W="\[\033[0;37m\]"    # white

# empahsized (bolded) colors
MK="\[\033[1;30m\]"
MR="\[\033[1;31m\]"
MG="\[\033[1;32m\]"
MY="\[\033[1;33m\]"
MB="\[\033[1;34m\]"
MM="\[\033[1;35m\]"
MC="\[\033[1;36m\]"
MW="\[\033[1;37m\]"

# background colors
BGK="\[\033[40m\]"
BGR="\[\033[41m\]"
BGG="\[\033[42m\]"
BGY="\[\033[43m\]"
BGB="\[\033[44m\]"
BGM="\[\033[45m\]"
BGC="\[\033[46m\]"
BGW="\[\033[47m\]"
## 文件 $HOME/.bashrc,设置提示符及其解释
###### PROMPT ######
# Set up the prompt colors
source $HOME/.bash/term_colors
PROMPT_COLOR=$G
if [ ${UID} -eq 0 ]; then
  PROMPT_COLOR=$R ### root is a red color prompt
fi

#t Some good thing about this prompt:
# (1) The time shows when each command was executed, when I get back to my terminal
# (2) Git information really important for git users
# (3) Prompt color is red if I'm root
# (4) The last part of the prompt can copy/paste directly into an SCP command
# (5) Color highlight out the current directory because it's important
# (6) The export PS1 is simple to understand!
# (7) If the prev command error codes, the prompt '>' turns red
export PS1="\e[42m\t\e[m$N $W"'$(__git_ps1 "(%s) ")'"$N$PROMPT_COLOR\u@\H$N:$C\w$N\n\["'$CURSOR_COLOR'"\]>$W "
export PROMPT_COMMAND='if [ $? -ne 0 ]; then CURSOR_COLOR=`echo -e "\033[0;31m"`; else CURSOR_COLOR=""; fi;'

命令行提示符之外的其他方面的配置,在Bash方面,我主要设置了一些命令行补全(completion)和别名设置(alias):

## git alias
alias g=git
alias gro='git r origin/master'
alias grio='git r -i origin/master'
alias gric='git r --continue'
alias gria='git r --abort'


## ls aliases
alias ls='ls -G'
alias la='ls -la'
alias ll='ls -l'


## git completion,请参考https://github.com/git/git/blob/master/contrib/completion/git-completion.bash
source ~/.git-completion.bash

Zsh的配置就容易得多了,而且是模块化的。基本上就是安装一个配置的框架,然后选择插件和主题即可。具体来说,我的Zsh命令行提示符配置步骤包括以下三步。

第一,安装oh-my-zsh。这是一个对Zsh进行配置的常用开源框架。

brew install zsh

第二,安装powerline字体,供下一步使用。

brew install powerlevel9k

第三,在~/.zshrc中配置ZSH_THEME,指定使用powerlevel9k这个主题。

ZSH_THEME="powerlevel9k/powerlevel9k"

命令行提示符以外的其他配置,主要是通过安装和使用oh-my-zsh插件的方式来完成。下面是我使用的各种插件,供你参考。

## 文件~/.zshrc.sh 中关oh-my-zsh的插件列表,具体插件细节请参考https://github.com/robbyrussell/oh-my-zsh,以及使用Web搜索查询
plugins=(
  git
  z
  vi-mode
  zsh-syntax-highlighting
  zsh-autosuggestions
  osx
  colored-man-pages
  catimg
  web-search
  vscode
  docker
  docker-compose
  copydir
  copyfile
  npm
  yarn
  extract
  fzf-z
)

source $ZSH/oh-my-zsh.sh

至于Fish的配置,和Zsh差不多,也是安装一个配置的框架,然后选择插件和主题即可。在配置命令行提示符时,主要步骤包括以下两步。

第一,安装配置管理框架oh-my-fish:

curl -L https://get.oh-my.fish | fish

第二,查看、安装、使用oh-my-fish的某个主题,主题会自动配置好命令行提示符:

omf theme
omf install <theme>
omf theme <theme>

## 我使用的是bobthefish主题
omf theme bobthefish

这里有一篇不错的关于使用oh-my-fish配置的文章,供你参考。

Fish的其他方面的配置,也是使用oh-my-fish配置会比较方便。关于具体的配置方法,建议你参照官方文档

关于环境的最后一个配置,是远程SSH的处理。

第四步,远程SSH的处理

SSH到其他机器,是开发人员的常见操作,最大的痛点是,怎样保持多次连接的持久性。也就是说,连接断开以后,远端的SSH进程被杀死,之前的工作记录、状态丢失,导致下一次连接进去需要重新设置,交易花销太大。有两类工具可以很好地解决这个问题。

第一类工具是Tmux或者Screen,这两个工具比较常见,用来管理一组窗口。

接下来,我以Tmux为例,与你描述其工作流程:首先SSH到远程服务器,然后用远程机器上的Tmux Client连接到已经运行的Tmux Session上。SSH断开之后,Tmux Client被杀死,但Tmux Session仍然保持运行,意味着命令的运行状态继续存在,下次SSH过去再使用Tmux Client连接即可。

如果你想深入了解Tmux的概念和搭建过程,可以参考这篇文章

第二类是一个保持连接不中断的工具,移动Shell(Mobile Shell)。这也是我目前唯一见到的一个。这个工具是MIT做出来的,知道的人不多,是针对移动设备的网络经常断开设计的。

它的具体原理是,每次初始登录使用SSH,之后就不再使用SSH了,而是使用一个基于UDP的SSP协议,能够在网络断开重连的时候自动重新连接,所以从使用者的角度来看就像从来没有断开过一样。

接下来,我以阿里云ECS主机、运行Ubuntu18.04为例,与你分享Mosh+Tumx的具体安装和设置方法。

第一,服务器端安装并运行Mosh Server。

junge@iZ4i3zrhuhpdbhZ:~$ sudo apt-get install mosh

第二,打开服务器上的UDP端口60000~61000。

junge@iZ4i3zrhuhpdbhZ:~$ sudo ufw allow 60000:61000/udp

第三,在阿里云的Web界面上修改主机的安全组设置,允许UDP端口60000~61000。

第四,在客户端(比如Mac上),安装Mosh Client。

jasonge@Juns-MacBook-Pro-2@l$ brew install mosh

第五,客户端使用Mosh,用与SSH一样的命令行连接到服务器。

jasonge@Juns-MacBook-Pro-2@l$ mosh junge@<server-ip-or-name>

下面这个录屏演示的是,我日常工作中使用Mosh + Tmux的流程。期间我会断开无线网,你可以看到Mosh自动连接上了,就好像来没有断过一样。

小结

今天,我与你分享的是使用命令行工具工作时,涉及的环境配置问题。

首先,我与你介绍了选择模拟终端、选择和配置Shell的重要准则,并结合案例给出了具体的工具和配置方法,其中涉及的工具包括iTerm2、Bash、Zsh、Fish等。然后,我结合远程SSH这一常见工作场景,给出了使用Tmux和Mosh的优化建议。

掌握了关于环境配置的这些内容以后,在下一篇文章中,我将与你介绍具体命令行工具的选择和使用。

其实,我推荐开发者多使用命令行工具,并不是因为它们看起来炫酷,而是它们确实可以帮助我们节省时间、提高个人的研发效能。而高效使用命令行工具的前提,是配置好环境。

以Mosh为例,我最近经常会使用iPad SSH到远端服务器做一些开发工作。在这种移动开发的场景下,iPad的网络经常断开,每次重新连接开销太大,基本上没办法工作。于是,我最终发现了Mosh,并针对开发场景进行了设置。现在,每次我重新打开iPad的终端时,远程连接自动恢复,好像网络从没有断开过一样。这样一来,我就可以在移动端高效地开发了。

而对研发团队来说,如果能够对命令行工作环境进行优化和统一,毫无疑问会节省个人选择和配置工具的时间,进而提升团队的研发效能。

思考题

你觉得Tmux和Screen的最大区别是什么?是否有什么场景,我们必须使用其中的一个吗?

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

评论