源码解读——ohmyzsh
最近在做一个 shell 项目——NOX,在开发过程中遇到了一些设计方面的问题。为了能够得到一些灵感,我将注意力放到了 github 上 star 数量最高的 shell 项目——ohmyzsh。本文将记录我阅读 ohmyzsh 源码后,对于其设计的一些理解。
概述
Ohmyzsh(下文简称 OMZ) 是一个由开发者社区驱动的开源框架,用于管理我们的 zsh 配置。OMZ 提供了大量的插件,能够有效提升我们的命令行工作效率。此外,OMZ 提供了大量的主题,通过主题配置我们可以将终端打造得极具极客范!
目录结构
我们首先来看一下 OMZ 的目录结构,如下图所示。根据
.gitignore
中的定义,我们可以大概能够猜测到:
cache
目录用于存放 OMZ 运行时产生的缓存文件。cache
目录不加入 git 管理。custom
目录用于存放用户自定义的一些插件或主题,该目录下默认有两个子目录plugins
和themes
分别用户存放插件和主题。比如:我们可以将https://github.com/zsh-users/zsh-completions
克隆到custom/plugins
目录下,作为自定义的插件集合。custom
目录不加入 git 管理。lib
目录下的一系列文件定义了诸多 shell 工具方法。下文我们将具体进行介绍。log
目录应该用户存放 OMZ 运行时产生的日志缓存文件。log
目录不加入 git 管理。plugins
目录存放了 OMZ 项目所管理的插件。该目录下的子目录数量庞大,其中每一个子目录对应一个工具,如:xcode
目录对应 Xcode,该目录下存放的文件可分为两类:一是以_
为前缀的自动补全文件;二是以.plugin.zsh
为后缀的插件文件。我们可以通过设置.zshrc
中的plugins
数组变量来选择加载的插件。templates
目录存放了一个 zsh 配置文件的模板。themes
目录存放了大量主题,文件命名方式遵循主题名称
+.zsh-theme
的规则。我们可以通过设置.zshrc
中的ZSH_THEME
变量来选择主题。tools
目录包含了一系列 OMZ 系统相关的重要脚本,如:安装、卸载、检查更新、执行更新等。
安装方式
从 tools/install.sh
脚本中,我们能够看出 OMZ
的安装过程可以分为以下五个步骤:
- 设置变量
- 环境检查
- 设置 OMZ
- 设置 zshrc
- 设置 shell
设置变量
第一步 设置变量 中设置的变量包含两类:基础设置相关的变量、执行选项相关的变量。
基础设置相关的变量包括以下这些:
ZSH
:表示 OMZ 仓库的路径,默认是$HOME/.oh-my-zsh
。REPO
:表示仓库名称,默认是ohmyzsh/ohmyzsh
。REMOTE
:表示 OMZ 仓库的远程地址,默认是https://github.com/${REPO}.git
。BRANCH
:表示安装 OMZ 时所对应的仓库分支,默认是master
。
执行选项相关的变量包括以下这些:
CHSH
:表示安装 OMZ 时是否会切换默认的 shell,默认是yes
。RUNZSH
:表示是否会在安装后运行 zsh,默认是yes
。KEEP_ZSHRC
:表示是否会替换已有的.zshrc
,默认是no
。
在执行 install.sh
脚本时,我们可以通过传入特定选项来设置执行选项相关的变量。如:
--skip-chsh
选项:将CHSH
设置为no
。--unattended
选项:将CHSH
和RUNZSH
均设置为no
。--keep-zshrc
选项:将KEEP_ZSHRC
设置为yes
。
环境检查
第二步 环境检查 中主要判断了两种情况:
- zsh 未安装的情况下,退出 OMZ 的安装
- OMZ 已安装的情况下,退出 OMZ 的安装
设置 OMZ
第三步 设置 OMZ 的任务主要是根据第一步中所设置的变量
REPO
、REMOTE
、ZSH
,通过
git
将远程仓库克隆到本地。
设置 zshrc
第四步 设置 zshrc,顾名思义,就是设置
~/.zshrc
文件。这里会用到第一步中所设置的变量
ZSH
和 KEEP_ZSHRC
。
当 KEEP_ZSHRC
为 yes
时,则跳过本步骤。
当 KEEP_ZSHRC
为 no
时,会先将原有的
~/.zshrc
备份为
~/.zshrc.pre-oh-my-zsh
,用于卸载时进行恢复。然后将
templates
目录下的 zshrc.zsh-template
拷贝为
~/.zshrc
,同时将 ZSH
变量的设置导入至
~/.zshrc
中。
~/.zshrc
设置完之后,其内部默认开启的配置并不多,如下所示。
1 | # Path to your oh-my-zsh installation. |
~/.zshrc
中定义的命令。OMZ 默认在
~/.zshrc
中设置了
ZSH
、ZSH_THEME
、plugins
三个变量,此外还定义了每次启动 zsh 时执行 oh-my-zsh.sh
脚本,对 OMZ 进行初始化。
设置 shell
第五步 设置 shell
的任务主要是根据第一步中所设置的变量 CHSH
来决定是否将默认的 shell 切换成 zsh。
OMZ 初始化
在上一节 安装方式 的 设置 zshrc
小结中,我们提到每次启动 zsh 时都会执行 oh-my-zsh.sh
脚本对
OMZ 进行初始化。那么这个初始化过程具体包含哪些内容呢?如下所示为
oh-my-zsh.sh
的源代码。
OMZ 初始化的主要步骤包括:
- 默认路径设置,如:
ZSH
、ZSH_CACHE_DIR
、fpath
、ZSH_CUSTOM
、ZSH_COMPDUMP
等 - 检查更新
- 初始化补全系统,包括:预设
fpath
变量、创建.zcompdump
文件等 - 加载 lib
- 加载插件
- 加载自定义配置
- 加载主题
详细初始化过程可以阅读如下所示的源代码。
1 | # 1. 如果 `ZSH` 变量没有设置,则将 `ZSH` 设置为当前 `oh-my-zsh.sh` 脚本所在的目录路径 |
更新策略
由于安装 OMZ 时,OMZ 会在 ~/.zshrc
中加入一行代码
source $ZSH/oh-my-zsh.sh
,从而使得每次启动 zsh 会都会执行
oh-my-zsh.sh
中的代码。oh-my-zsh.sh
中通过
DISABLE_AUTO_UPDATE
变量来判断是否执行更新检查,然后由
check_for_upgrade.sh
来决定是否进行更新,如下所示:
1 | if [ "$DISABLE_AUTO_UPDATE" != "true" ]; then |
DISABLE_AUTO_UPDATE
默认未进行初始化,所以每次启动 zsh
都会进行更新检查,用户可以通过在 ~/.zshrc
去掉对
DISABLE_AUTO_TITLE="true"
的注释来关闭更新检查。
下面,我们依次来介绍 OMZ 中的更新检查和更新执行。
更新检查
OMZ 更新检查的主要逻辑包括以下这些步骤:
- 读取
cache/.zsh-update
中的LAST_EPOCH
变量,如果文件不存在或LAST_EPOCH
的值为空,则写入新值并退出。LAST_EPOCH
表示上一次更新的时间(单位:从 1970年1月1日至当前的天数)。 - 读取更新间隔值
UPDATE_ZSH_DAYS
,默认值为 13。如果上次LAST_EPOCH
的更新时间与当前相隔 13 天及以上,那么继续后面的步骤;否则,退出。 - 根据
DISABLE_UPDATE_PROMPT
的值决定是否请求用户同意更新。采用交互式的方式请求用户同意。如果同意更新,则执行更新,更新完成后更新cache/.zsh-update
中上次更新的时间LAST_EPOCH
。
更新执行
具体的更新操作非常简单,就是通过 git pull --rebase
的方式获取最新的代码。
插件
插件是 OMZ 所提供的最重要的功能之一。插件主要位于 OMZ 的
plugins
目录下,每一个插件都有一个维护者,因此 OMZ
是一个社区驱动的开源项目。
那么插件究竟干了些什么呢?我们以
plugins/git/git.plugin.zsh
为例,进行分析。
总体而言,插件提供了三方面的功能。
- 函数
- alias
- 自动补全
对于函数,插件中提供了如下所示的一些函数,这些函数我们可以在 shell 中直接调用。
1 | function current_branch() { |
对于 alias,插件中则定义大量缩写以供用户快速调用命令,如下所示:
1 | alias gc='git commit -v' |
与此同时,插件还为函数定义了自动补全函数,如下所示定义
ggpnp
函数的自动补全与 git checkout
相同。
1 | function ggpnp() { |
此外,OMZ 还会提供特定的自动补全文件,一般以 _
为前缀,后跟具体的命令名称,如:_pod
为 pod
提供了自动补全定义。
我们可以对 ~/.zshrc
的 plugins
变量进行修改,选择我们想要加载的插件。
关于 zsh 自动补全的细节,本文不作具体介绍,可以参考 《Zsh 自动补全脚本入门》 一文。
主题
主题主要用于修改 zsh 提示符和某些程序的外观,还有一些其他的行为,如:终端的 tab 和窗口标题。
主题通过设置各种变量来控制 zsh
提示符的外观,包括:PROMPT
、RPRPROMT
、LSCOLORS
、LS_COLORS
等变量;此外,主题还能够安装 hook 方法,从而提供更深层次的修改。
我们可以对 ~/.zshrc
的 ZSH_THEME
变量进行修改,选择我们想要的主题。
关于主题的具体实现,后面有时间写一篇文章来进行介绍。
总结
总体而言,从设计角度而言,OMZ 非常简单,它使用清晰的目录结构管理插件、主题。OMZ 的安装和更新也非常简单,没有复杂的机制。由于 OMZ 提供了巨量的插件和主题,能够极大地提升用户的工作效率,从而受到了用户的热烈追捧。
预览: