Skip to content

zsh 配置指南

按照我的仓库中的提示安装配置. 如果你感兴趣其中的原理以及插件/工具的使用方法, 或者感兴趣我的配置理念, 请往下看.

Oh My Zsh是 zsh 框架中最火的一个, 其可以开箱即用的特性让很多人喜欢. 但它的默认配置实在不够 optimized, 例如自动转义拖慢粘贴的速度, prompt 加载慢 (尤其是在 git 仓库中), 以及过多的插件拖慢启动速度等等.

经过一番调研, 我选择了 Zinit 作为我的 zsh 插件管理器. 它有一个强大的特性, 可以异步加载插件, 也就是说你可以优先加载最可见的插件, 如语法高亮, prompt theme 等, 其他不那么重要的插件可以后台异步加载. 这个特性赋予了我们极大的加载灵活度和速度.

借此机会我也小小的研究了一下 shell 的各种插件, 以及一些好用的 CLI 工具.

不少人会在 GitHub 上创建一个 dotfiles 仓库来方便管理和同步不同本地机器和不同服务器上的环境配置. 关于具体如何同步有不同的流派, 有人使用现成的工具例如 yadm, dotbot 等; 有人使用自己编写的脚本. 但无论用什么办法, 他们都使用软链接或者直接覆盖 .zshrc 的方式来同步配置. 这在我看来实在不够优雅 — 我不能临时修改这些文件来实现一些只有本机而不是所有机器都需要的配置. 考虑到非常非常多工具都需要配置 .zshrc 来启用, 这个场景发生的频率还不算低.

所以我希望我的配置同步方式符合一些原则:

  • 能够配置”本地的” .zshrc, 我的配置应该是加载上去而不是覆盖
  • 能够快速安装, 不需要额外操作
  • 足够简单易懂, 避免复杂性带来的维护问题
  • 跨平台使用

可惜的是, 我搜索了很多方案都无法满足我的要求. 所以我一直不想去认真的配置我的 shell. 直到我偶然发现了这篇文章, 通过 init.zsh 分离通用配置和本地配置, 通过 bootstrap.sh 一键安装, 完美符合了我的需求.

因此我顺着这个思路开始了我的 dotfiles 的搭建.

.
├── bootstrap.sh
├── install.sh
└── zsh
├── init.zsh
├── plugins.zsh
└── ...

运行 bootstrap.sh 即可自动安装我的 zsh 配置, 由于我将其上传到了 GitHub, 我可以直接使用 curl 来获取脚本并运行, 非常方便. 其他重要文件主要要: install.sh 安装了一些必要的插件和实用的工具, init.zsh 是配置入口, 会调用 zsh 文件夹下的其他配置.

具体而言, bootstrap.sh 会先检查本地是否存在 dotfiles 仓库, 没有就先clone 下来, 然后运行install.sh脚本安装插件和工具, 最后在.zshrc最后添加source init.zsh的命令. 对系统的侵入性极小, 只需要注释掉source 就可以完全禁用掉这个仓库中的配置.

我们来一步一步构建 zsh 的配置. 如果看完你觉得我的配置很好或者你懒得看, 你可以直接根据我的仓库中的指示一键安装. 但我也强烈建议你自己动手构建适合你自己的配置. 当然也欢迎给我的配置提意见.

为了保持文章的简洁, 我只会说明 high-level 的配置思路. 你应当对照着源码以及每个插件/工具的官方文档阅读这些内容. 源码中都有详细的注释.

zinit 用来自动安装插件以及异步加载插件. Super fast!

Terminal window
ZINIT_HOME="${XDG_DATA_HOME:-${HOME}/.local/share}/zinit/zinit.git"
source "${ZINIT_HOME}/zinit.zsh"

推荐使用:

  • powerline10k
  • pure
  • starship

我使用 pure:

Terminal window
zinit ice compile'(pure|async).zsh' pick'async.zsh' src'pure.zsh'
zinit light sindresorhus/pure

zinit 命令解释:

  • compile: 编译 zsh 文件使其运行更快.
  • pick: 指定插件的主文件 (用于 source).
  • src: 指定额外需要 source 的文件.

zoxide用于快速跳转文件夹, 代替 cd.

Terminal window
zinit ice wait lucid as"command" from"gh-r" \
atclone"./zoxide init zsh > init.zsh" \
atpull"%atclone" src"init.zsh" atload"unalias zi"
zinit light ajeetdsouza/zoxide

解释:

  • wait lucid: Turbo mode.
  • as"command": 这是一个可执行文件, 不是需要 source 的插件.
  • from"gh-r": 从 GitHub Release 获取插件.
  • atclone, atpull: 在第一次 clone, 或者后面通过 pull 更新后执行命令. 此处用 zoxide init zsh 生成 zoxide 的激活脚本, 方便我们 source.
  • src: 指定加载插件时 source 我们上一步生成的激活脚本.
  • atload"unalias zi": 避免 zinit 的alias zi 覆盖掉 zoxide 的zi.

使用时可以输入文件夹的关键词从任意文件夹跳转到任意文件夹, 而不用一次一次的补全目录. zoxide 会在每次目录切换时自动记录完整的路径并维护一个 score 来排序不同的目录. 在跳转时, zoxide 会要求关键词按顺序出现在目录中, 例如 z some / long path 会匹配到 /foo/some/bar/long/path 但不会匹配 /some/path/long.

命令 z 在跳转时会排除当前的目录, 这意味着当你跳转错了时, 可以重新执行一遍来跳转到下一个匹配的目录.

命令 z 完全兼容 cd, 你可以将 z 当做 cd 来使用, 例如 z ..切换到上级目录, z -切换到上个目录.

zoxide 也提供一个交互式的面板供选择目录, 你可以通过 zi foo 或者 z foo<SPACE><TAB> 来打开这个面板.

fzf 是一个强大又好看的模糊搜索神器, 它可以搜索和筛选任何”列表”, 例如做命令历史搜索, 查找文件, 查找 commit 记录等等.

Terminal window
zinit ice wait lucid as"command" from"gh-r" \
atclone"./fzf --zsh > init.zsh" \
atpull"%atclone" src"init.zsh"
zinit light junegunn/fzf

解释:

同上, 原理一样.

基本用法: fzf 从 STDIN 读取文本, 打开一个面板, 在面板中输入关键词来做模糊搜索, fzf 会将选择的结果输出到 STDOUT. 例如你可以使用ps aux | fzf来模糊搜索一个进程.

搜索面板的使用:

  • Ctrl-N/Ctrl-P上下移动.
  • Enter 选择项目.
  • Ctrl-C/Ctrl-G/Esc 退出面板.
  • 也可以直接使用鼠标来选择.

历史命令搜索:

大大提高效率. 不用疯狂按上箭头来找历史命令. 默认快捷键是Ctrl-R.

文件搜索:

Ctrl-T

使用 fzf 作为 zsh 的默认补全前端.

Terminal window
zinit ice wait lucid atinit"zicompinit; zicdreplay"
zinit light Aloxaf/fzf-tab

解释:

根据fzf-tab 的文档, fzf-tab 需要在执行 compinit之后加载.

  • zicompinit: 等同于执行 autoload compinit; compinit. compinit 是 zsh 的补全系统初始化命令, 它会加载补全定义并设置补全系统, 这个命令需要在使用任何补全功能(如 fzf-tab)之前运行.
  • zicdreplay: 解决了一个关键的时序问题:很多插件在加载时会使用 compdef 来定义命令补全,但这些 compdef 命令必须在 compinit 初始化之后才能工作。zinit 通过先记录这些 compdef 命令,然后在 compinit 执行完后用 zicdreplay 重放它们.

使用/键来连续补全 (在补全一个长路径时很有用)

你可以使用 z <TAB> 来看 fzf-tab 的效果.

高亮你的命令, 主要是好看. 也可以快速看出你的命令是否正确(存在红色的部分就是有问题).

Terminal window
zinit ice wait lucid
zinit light zdharma-continuum/fast-syntax-highlighting

在 shell 中工作时, 可能经常要执行某条命令(例如运行python main.py), 有了zsh-autosuggestion, 当你输入 p时, zsh 就会向你建议整条命令, 你可以按 (或者 Ctrl-F) 来接受这条建议. 熟练之后, 就可以在修好 bug 后快速 p<Ctrl-F><Enter>重启实验了.

Terminal window
# first try the most recent command that follows the same context as current command, then search history, finally fallback to completion
ZSH_AUTOSUGGEST_STRATEGY=(match_prev_cmd history completion)

一些额外的 completions 定义

oxidized tools

用来代替 cat. 有高亮, 分页等.

Terminal window
bat test.md

可以配置manual page 使用 bat 作为 pager.

Terminal window
export MANPAGER="sh -c 'col -bx | bat -l man -p'"
Terminal window
# 列出目录下所有文件和文件夹的大小
dua
# trick: 忽略 `.` 开头的文件(夹)
dua *
# 交互模式, 可以进入子文件夹查看哪些文件占用空间
dua i
Terminal window
dysk -c mount_point+use+free+size+type+disk+filesystem

由于这个软件不能在 macOS 上使用, 为了保证我的 dotfiles 的跨平台性, 其不会自动安装 dysk. 你可以通过 cargo binstall dysk 手动安装.

替换 ls.

Terminal window
### aliases for eza
alias ls="eza"
alias ll="eza -al --icons --group --binary --time-style=long-iso --group-directories-first"
alias tree="eza --tree --icons --group-directories-first"

Simple search: fd foo Regular expression search: fd 'foo.*bar' Specifying the root directory: fd foo /etc Searching for a particular file extension: fd foo -e md Hidden and ignored files: -H, -I

Terminal window
ouch d
ouch c
ouch l
Terminal window
procs
procs zsh
procs --tree --watch

全文搜索工具.

使用 tealdeer 版本的前端实现.

用于代替 man.

代码行数统计.

也是 oxidized tool, 不过已经在配置 zsh一节介绍过.

详见源码.

Terminal window
HISTFILE=~/.zsh_history
HISTSIZE=50000
SAVEHIST=$HISTSIZE
setopt extended_history
setopt inc_append_history
setopt share_history
setopt hist_ignore_space
setopt hist_ignore_dups

加入一些常用的 path.

Terminal window
# required by cargo
export PATH="$HOME/.cargo/bin:$PATH"
export PATH="$HOME/.local/bin:$PATH"
export PATH="$HOME/bin:$PATH"