Files
hugo-blog/content/posts/my-zsh-terminal-setup.md
2026-05-28 22:20:00 +08:00

446 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
date: '2026-05-28T22:05:00+08:00'
draft: false
title: '我的 Zsh 终端配置Zinit、Powerlevel10k 与日常效率优化'
tags:
- zsh
- terminal
- dotfiles
- zinit
- powerlevel10k
categories:
- 技术
---
## 前言
一个好用的 Shell 配置,不应该只是“看起来很酷”。它更应该做到三件事:
1. **启动快**:进入终端后立刻可输入命令,而不是等待一堆插件初始化。
2. **提示准**当前目录、Git 状态、命令执行结果、语言环境等信息一眼可见。
3. **操作顺手**:常用命令短、路径跳转快、补全和历史建议足够智能。
这篇文章记录我的 Zsh 配置思路。整体方案是:**Zinit 管理插件Powerlevel10k 负责提示符Oh My Zsh 提供成熟插件生态,再配合 eza、yazi、pnpm、n 等工具优化日常开发体验**。
<!--more-->
## 整体结构
我的 Zsh 配置主要由两个文件组成:
```text
~/.zshrc # 主配置环境变量、zinit、插件、别名、函数
~/.p10k.zsh # Powerlevel10k 主题配置
```
加载顺序大致如下:
```text
启动 Zsh
|
v
Powerlevel10k instant prompt
|
v
基础环境变量与 Shell options
|
v
加载 zinit
|
v
加载 OMZ library / Powerlevel10k / 插件
|
v
source ~/.p10k.zsh
|
v
注册 alias / function / PATH
```
这种拆分的好处是:`.zshrc` 保持“启动流程与插件管理”,`.p10k.zsh` 专注“提示符长什么样”。以后更换主题时,也不会影响插件和别名体系。
## 用 instant prompt 提升启动体验
配置最顶部是 Powerlevel10k 的 instant prompt
```zsh
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
fi
```
它的作用是:在完整配置加载完成之前,先显示一个临时 Prompt让终端看起来“秒开”。
同时我设置了:
```zsh
typeset -g POWERLEVEL9K_INSTANT_PROMPT=quiet
```
这样可以减少 instant prompt 对启动时输出内容的干扰。对于需要频繁打开新终端、tmux pane 或远程会话的人来说,这一点体验提升很明显。
## 基础环境与 Zsh options
```zsh
path=(~/.local/bin $path)
typeset -x XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
setopt correct
setopt interactive_comments
```
这里做了几个基础设定:
| 配置 | 作用 |
|------|------|
| `~/.local/bin` 前置到 `PATH` | 优先使用用户级安装的命令 |
| `XDG_CONFIG_HOME` | 统一配置目录,默认指向 `~/.config` |
| `setopt correct` | 命令输错时给出纠正建议 |
| `setopt interactive_comments` | 交互式 Shell 中也允许使用 `#` 注释 |
`interactive_comments` 很适合在终端里临时记录命令意图,比如粘贴多行命令时可以保留注释说明。
## 为什么选择 Zinit
插件管理器我使用的是 Zinit
```zsh
ZINIT_HOME="${XDG_DATA_HOME:-${HOME}/.local/share}/zinit/zinit.git"
[[ ! -d "${ZINIT_HOME}" ]] && mkdir -p "$(dirname "${ZINIT_HOME}")"
[[ ! -d "${ZINIT_HOME}/.git" ]] && git clone https://gh-proxy.org/https://github.com/zdharma-continuum/zinit.git "${ZINIT_HOME}"
source "${ZINIT_HOME}/zinit.zsh"
```
Zinit 的核心价值是:**可以精细控制插件加载时机**。比如有些插件必须同步加载,有些插件可以延迟 1 秒、2 秒、3 秒再加载;对于启动速度敏感的 Shell 配置来说,这比简单地 `source` 一堆脚本更可控。
配置里还使用了 GitHub 代理地址,主要是为了在国内网络环境下更稳定地安装 Zinit。你也可以按自己的网络情况替换为官方仓库地址。
## 先加载 Oh My Zsh 的基础库
我没有直接使用完整的 Oh My Zsh 框架,而是通过 Zinit 按需加载其中的 library 和 plugin。
同步加载的 OMZ library
```zsh
for i in theme-and-appearance.zsh git.zsh prompt_info_functions.zsh history.zsh; do
zinit snippet "OMZL::${i}"
done
```
延迟加载的 OMZ library
```zsh
for i in completion.zsh key-bindings.zsh; do
zinit ice lucid wait="1"
zinit snippet "OMZL::${i}"
done
```
这里的思路是:
| 类型 | 加载策略 | 原因 |
|------|----------|------|
| theme / git / prompt / history | 同步加载 | 主题和提示符依赖,越早越好 |
| completion / key-bindings | 延迟加载 | 对首屏 Prompt 不是硬依赖 |
补全初始化则交给:
```zsh
zpcompinit
zpcdreplay
```
这样可以配合 Zinit 的补全 replay 机制,让插件延迟加载后仍然能正确注册补全。
## Powerlevel10k两行式 Prompt
主题使用 Powerlevel10k
```zsh
zinit ice depth=1
zinit light romkatv/powerlevel10k
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
```
`depth=1` 可以减少 clone 体积,只拉取必要历史。
我的 `.p10k.zsh` 是通过 `p10k configure` 生成的 rainbow 风格配置,主要特点是:
| 项目 | 配置 |
|------|------|
| 字体 | Nerd Font v3 |
| 风格 | powerline + rainbow |
| Prompt | 两行式 |
| 分隔符 | angled separators |
| 图标 | small icons / many icons |
| 显示密度 | sparse、concise |
左侧 Prompt 保留最核心的信息:
```zsh
POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(
os_icon
dir
vcs
newline
prompt_char
)
```
右侧 Prompt 则放一些上下文信息例如命令状态、执行耗时、后台任务、Python 虚拟环境、Node 环境、Kubernetes context、Terraform workspace、云 CLI profile、时间等。
这种布局的优点是:**左边只显示当前操作最常用的信息,右边显示“有就看、没有也不影响”的上下文**。两行式 Prompt 也能避免路径太长时挤压命令输入区域。
## 插件设计:按“必要程度”延迟加载
我的插件并不是一股脑同步加载,而是按使用频率和必要程度分层。
| 插件 | 加载方式 | 用途 |
|------|----------|------|
| `command-not-found` | `wait=0` | 命令不存在时提示可安装的软件包 |
| `colored-man-pages` | `wait=3` | 彩色 man 页面 |
| `fast-syntax-highlighting` | async | 命令行语法高亮 |
| `zsh-autosuggestions` | async + `atload` | 基于历史记录的命令建议 |
| `sudo` | `wait=2` | 双击 `Esc` 给当前命令加 `sudo` |
| `z` | `wait=1` | 根据访问频率跳转目录 |
| `git` | `wait=1` | Git 常用别名与函数 |
| `extract` | `wait=1` | 一条命令解压多种格式 |
| `kubectl` | `wait=1` | Kubernetes 别名、补全与辅助函数 |
其中最影响交互体验的是两个插件:
```zsh
zinit light zdharma-continuum/fast-syntax-highlighting
zinit light zsh-users/zsh-autosuggestions
```
- `fast-syntax-highlighting`:输入命令时实时高亮,能提前发现命令、路径、参数是否写错。
- `zsh-autosuggestions`:根据历史记录给出灰色建议,按右方向键即可补全。
这两个插件会让命令行从“纯文本输入框”变成“带反馈的交互环境”。
## 文件列表eza 优先ls 兜底
我把常用 `ls` 系列命令封装成了别名,如果系统安装了 `eza`,就优先使用 `eza`
```zsh
if [[ -n "$(command -v eza)" ]]; then
DISABLE_LS_COLORS=true
alias ls='eza --color=auto'
alias l='eza -lbah --icons=auto'
alias la='eza -labgh --icons=auto'
alias ll='eza -lbg --icons=auto'
alias lt='eza -lTbg --icons=auto'
alias lsa='eza -lbagR --icons=auto'
alias lst='eza -lTabgh --icons=auto'
else
alias ls='ls --color=auto'
alias l='ls -lah'
alias la='ls -lAh'
alias ll='ls -lh'
alias lsa='ls -lah'
alias lst='tree -pCsh'
fi
```
常用别名含义如下:
| 别名 | 作用 |
|------|------|
| `l` | 长列表,显示隐藏文件、人类可读大小、图标 |
| `la` | 类似 `l`,但更适合查看权限和分组信息 |
| `ll` | 简洁长列表 |
| `lt` | 树状展示目录 |
| `lsa` | 递归列出所有文件 |
| `lst` | 树状递归展示并包含隐藏文件 |
这里的关键是“有增强工具就增强,没有就优雅降级”。配置可以在新机器上直接使用,不会因为缺少 `eza` 导致 Shell 启动失败。
## 路径跳转与目录历史
```zsh
alias ...=../..
alias ....=../../..
alias .....=../../../..
alias ......=../../../../..
alias 1='cd -'
alias 2='cd -2'
alias 3='cd -3'
# ...一直到 9
```
这组别名解决两个常见问题:
1. 多级返回目录时不用反复输入 `cd ../../..`
2. 在多个目录之间来回切换时,可以直接用 `1``9` 访问目录栈。
配合 OMZ 的 `z` 插件,可以覆盖两类跳转场景:
- **已知相对路径**:使用 `...``....`
- **经常访问的目录**:使用 `z <keyword>`
## yazi退出文件管理器后自动 cd
配置中有一个很实用的 `y()` 函数:
```zsh
function y() {
local tmp cwd
tmp="$(mktemp -t yazi-cwd.XXXXXX)"
yazi "$@" --cwd-file="$tmp"
cwd="$(cat -- "$tmp")"
if [[ -n "$cwd" && "$cwd" != "$PWD" ]]; then
builtin cd -- "$cwd"
fi
rm -f -- "$tmp"
}
```
普通文件管理器的问题是:你在里面进入了某个目录,但退出后 Shell 仍然停留在原目录。这个函数通过 `--cwd-file` 记录 yazi 退出时所在目录,然后让当前 Shell 自动 `cd` 过去。
实际体验就是:
```bash
y
# 在 yazi 中进入目标目录
# 退出 yazi 后Shell 自动切换到那个目录
```
这比在终端里反复 `cd``ls`、再 `cd` 要顺手很多。
## Git、解压、sudo 与搜索
除了视觉和补全,我还保留了一些非常实用的小别名:
```zsh
alias _='sudo '
alias gc1='git clone --recursive --depth=1'
alias md='mkdir -p'
alias rd='rmdir'
alias afind='ack -il'
alias grep='grep --color=auto'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
```
| 别名 | 用途 |
|------|------|
| `_` | 快速给命令加 `sudo` 前缀 |
| `gc1` | 浅克隆并递归拉取子模块 |
| `md` | 创建多级目录 |
| `rd` | 删除空目录 |
| `afind` | 用 `ack` 搜索包含关键词的文件 |
| `grep` 系列 | 默认开启彩色输出 |
此外 OMZ 的 `extract` 插件也很省心:不需要记住 `tar.gz``zip``xz``bz2` 等格式分别怎么解压,统一用 `extract` 即可。
## 代理开关:为国内网络环境准备
```zsh
alias proxy-on='export http_proxy=http://127.0.0.1:7890 https_proxy=http://127.0.0.1:7890'
alias proxy-off='unset http_proxy https_proxy'
```
这两个别名适合临时让命令行工具走本地代理,例如拉取 GitHub 仓库、安装依赖、访问国外 API 等。
```bash
proxy-on
# 执行需要代理的命令
proxy-off
```
我没有把代理永久写进环境变量,因为那样容易影响内网服务、容器网络和一些只需要直连的命令。按需开启更可控。
## Node 与前端工具链
配置末尾还包含 pnpm 和 n 的环境变量:
```zsh
export PNPM_HOME="/root/.local/share/pnpm"
case ":$PATH:" in
*":$PNPM_HOME/bin:"*) ;;
*) export PATH="$PNPM_HOME/bin:$PATH" ;;
esac
export N_PREFIX="$HOME/n"
[[ :$PATH: == *":$N_PREFIX/bin:"* ]] || PATH+=":$N_PREFIX/bin"
```
这里有两个目的:
1. `pnpm` 的全局 bin 目录加入 `PATH`,方便直接运行全局安装的 CLI。
2. `n` 管理的 Node.js 路径加入 `PATH`,让当前 Shell 使用用户目录下的 Node 版本。
如果你也用 pnpm 和 n建议把它们放在 Shell 配置末尾,避免覆盖前面插件初始化所需的环境。
## 依赖清单
这套配置推荐安装的依赖如下:
| 依赖 | 必需 | 用途 |
|------|:----:|------|
| `zsh` | ✅ | Shell 本体 |
| `git` | ✅ | 安装 zinit、拉取插件 |
| `zinit` | ✅ | 插件管理器 |
| `powerlevel10k` | ✅ | Prompt 主题 |
| Nerd Font | ✅ | 正确显示图标和 powerline 符号 |
| `eza` | 可选 | 更现代的 `ls` 替代品 |
| `tree` | 可选 | `eza` 不存在时作为树状列表兜底 |
| `ack` | 可选 | `afind` 搜索别名 |
| `yazi` | 可选 | 终端文件管理器 |
| `kubectl` | 可选 | Kubernetes 补全与别名 |
| `command-not-found` | 可选 | 命令不存在时提示安装包 |
| `pnpm` | 可选 | Node 包管理器 |
| `n` | 可选 | Node 版本管理 |
最小安装只需要:`zsh``git`、Nerd Font。其他工具可以按需补齐。
## 可复用的安装步骤
如果要在新机器上复用这套配置,可以按这个流程:
```bash
# 1. 安装基础依赖
sudo apt update
sudo apt install -y zsh git
# 2. 可选增强工具
sudo apt install -y tree ack
# 3. 安装 eza / yazi / pnpm / n 等工具
# 不同发行版包名可能不同,可按实际系统调整
# 4. 放置配置文件
cp .zshrc ~/.zshrc
cp .p10k.zsh ~/.p10k.zsh
# 5. 切换默认 Shell
chsh -s "$(command -v zsh)"
```
首次进入 Zsh 时,配置会自动安装 Zinit并通过 Zinit 拉取 Powerlevel10k 和插件。
## 我的取舍
这套配置没有追求“插件越多越好”,而是围绕日常使用做了几个取舍:
1. **不完整引入 Oh My Zsh**:只按需加载 OMZ library 和 plugin减少框架开销。
2. **Prompt 信息分层**:左侧保留目录与 Git右侧显示环境上下文。
3. **增强工具必须有 fallback**`eza` 不存在就退回 `ls`,避免新机器启动失败。
4. **代理手动开关**:避免代理污染所有命令。
5. **插件延迟加载**:把首屏可交互时间放在第一优先级。
## 总结
我的 Zsh 配置可以概括为:
- **Zinit**:负责插件安装、延迟加载和补全 replay。
- **Powerlevel10k**:提供高信息密度、低干扰的两行 Prompt。
- **OMZ snippets**:按需复用 Oh My Zsh 的成熟插件生态。
- **eza / yazi / z / autosuggestions**:提升文件浏览、目录跳转和命令输入效率。
- **pnpm / n**:补齐前端和 Node.js 开发环境。
终端配置是一个长期迭代的系统。最好的配置不是最复杂的配置,而是能让你在每天打开终端时少想一步、少敲几个字、少等几百毫秒的配置。