坑边闲话:Zsh 的配置笔者用了许多年,丰富的生态和现代化的配置,使得 Zsh 的体验非常良好。2023 国庆之后,我把 Zsh 的配置用模块化的方式重新维护了一遍,更加贴合日常习惯。本文介绍我的 Zshell 配置文件,重点讲解总体架构和设计思路。

毫无疑问,Bash 依旧是 linux shell 编程的首选。但是,Zsh 的强大生态和丰富插件却吸引了越来越多用户去使用。

选择 Shell 类语言进行编程不是个好的方案,因为它在流程控制方面有先天的弱势,计算能力更是趋近于无。在进行编程时,我习惯用 Python 编码,其中会调用短的系统命令完成任务执行,但是将流程控制交给 Python 解释器。因此对我而言,选择什么 Shell 不是难题,因为我不用 Shell 编程。

Shell 是用户与 Linux 内核进行交互的重要中间件,Shell 一般通过 exec 系列命令生成新的进程执行 shell script,进而完成用户的意图。Shell 程序可以从命令行中获取参数、命令,也可以通过 stdin (一般是文件)获取输入,两者在功能上是等价的。

Zsh、Bash 等,均是 Shell 思想的具体实现,他们或许在某些语法上非常相似,但是彼此并不能互相完全兼容。比如 Zsh 中执行的 echo 命令,一般是内置的快速实现,而非 /bin/echo,这些小差异带来了许多选择上的麻烦。

Bash 风靡了这么多年,毫无疑问是所有机器上必装的软件,因此用 Bash 语法编写 shell script 是最稳妥的。这里不再赘述。本文想介绍日常交互式使用场景下的更佳选择:zsh.

本文以 Debian bookworm 作为基础系统进行讲解。

1. Zsh 环境配置基本框架·

本章介绍配置 Zsh 环境的基本思路,学习本章将有助于读者理解笔者的代码框架。

在进行配置学习之前,先安装系统基础组件:

1
2
sudo apt update
sudo apt install zsh git curl wget

随后笔者按照代码框架逐个介绍。

1. 安装插件: init_plugin·

手动管理 Zsh 插件比较繁琐,使用 Zim 可以有效降低维护负担。使用 zmodule 命令可以激活本地 Zsh 扩展。

因为依赖因袭的 oh-my-zsh 插件,所以也要安装 oh-my-zsh,不导入只引用。

防止迷惑

oh-my-zsh 是一套开源的 zsh 配置,内置多种主题,样式比较美观。

本文只使用 oh-my-zsh 提供的一些内部插件,所以即使不使用全部的 Oh-my-zsh 功能,也需要安装 Oh-my-zsh 的所有文件,毕竟它把所有插件全部放在了 plugins 目录下,而非插件单独成库。

1
2
3
4
5
zmodule ~/.local/share/oh-my-zsh/plugins/bundler
zmodule ~/.local/share/oh-my-zsh/plugins/dotenv
zmodule ~/.local/share/oh-my-zsh/plugins/rake
zmodule ~/.local/share/oh-my-zsh/plugins/rbenv
zmodule ~/.local/share/oh-my-zsh/plugins/ruby

Zim 项目本身提供了特别多的组建,通过 zmodule 命令可以迅速联机下载并安装。笔者使用了如下插件:

  • asciiship: Zsh prompt,适用于 ASCII 模式。
  • completion: 自动补全。
  • duration-info: 为 Zsh prompts 提供上次命令执行所耗费时间的信息。
  • environment: 设置通用 Zsh 内置环境选项,比如历史记录文件、自动 cd 目录等。
  • eriner: 笔者日常使用的主题,比较华而不实。
  • git-info: 将 git 存储库 status 信息提供给 Zsh prompt.
  • git: 提供了许多 git 子命令别名。
  • input: 为输入事件应用正确的绑定键。
  • prompt-pwd: 格式化当前工作目录以供 Zsh prompt 使用。
  • termtitle: 设置自定义终端标题。
  • utility: 为常用命令行程序添加别名和功能,比如为 lsgrepless 添加颜色等。

Zim 作为一个 Zsh 扩展管理,自然也支持第三方模块,笔者使用了如下第三方插件:

1. 设置合适的别名: init_alias·

通过 alias 命令为常用的组合设置别名,可以有效增加日常使用便捷性。但是此处不推荐对较为大众化的命令进行别名覆盖,否则容易出现意想不到的问题。但是,alias ip=ip -color 这种无伤大雅的别名覆盖并不会存在太多隐患。

1
2
3
4
5
6
7
8
9
10
11
alias ll='ls -lFh'
alias la='ls -al'
alias l='ls -1'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
alias dir='ls'
alias ip='ip -color'
if [[ $os_type != "OpenWrt" ]]; then
# alias vim='nvim'
fi

1. 识别系统类型: get_os_type·

可以使用 unamecat /etc/os-release 等方式查看系统类型,并将其设置为环境变量,方便后续操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
if [[ `uname -s` == "Linux" ]]; then
os_type=$(cat /etc/os-release | grep -i '^NAME')

# A. TrueNAS SCALE
if [[ `uname -r` == *"truenas"* ]]; then
os_type="TrueNAS_SCALE"

# B. OpenWrt
elif [[ $os_type == *"OpenWrt"* ]]; then
os_type="OpenWrt"

# C. Debian
elif [[ $os_type == *"Debian"* ]]; then
os_type="Debian"

# D. RedHat
elif [[ $os_type == *"Red Hat Enterprise Linux"* ]]; then
os_type="RedHat"

# E. Ubuntu
elif [[ $os_type == *"Ubuntu"* ]]; then
os_type="Ubuntu"

# F. FreeBSD
else [[ $os_type == *"FreeBSD"* ]]
os_type="FreeBSD"

fi
elif [[ `uname -s` == "Darwin" ]]; then
os_type="macOS"
else
os_type="unknown"
fi

1. 设置合适的环境变量·

为了提高脚本的适用性,这里使用之前定义的 os_type 环境变量进行了分支跳转。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if [[ $os_type == "TrueNAS_SCALE" ]]; then
# 补全 PATH 变量,否则普通 sudo 用户无法使用 zpool 命令
export PATH=$PATH:/sbin
export PATH=$PATH:/mnt/Intel_750_RAID-Z1/newton/bin

# 存储池相关目录
export doc='/mnt/Intel_750_RAID-Z1/Documents'

export soft='/mnt/Samsung_PM983A_RAID-Z1/Software'

export download="/mnt/Toshiba_MG06S_RAID-Z1/Downloads"
export xunlei="${download}/Xunlei_Downloads"
export byr="${download}/BYR_Download"
else
export app_data='/home/newton/h12-truenas-app_data'
export doc='/home/newton/h12-truenas-documents'

export soft='/home/newton/h12-truenas-software'

export download="/home/newton/h12-truenas-downloads"
export xunlei="${download}/Xunlei_Downloads"
export byr="${download}/BYR_Download"
fi

2. 全自动化配置·

最新的安装方式请参考项目主页

首先,要保证家目录有 .zshenv 文件。编辑文件 vim ~/.zshenv 并配置 XDGDesktop 相关的环境变量。可通过如下命令完成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
tee ${HOME}/.zshenv << EOF
# XDG 规范的路径
export XDG_CONFIG_HOME="$HOME/.config"
export XDG_DATA_HOME="$HOME/.local/share"
export XDG_CACHE_HOME="$HOME/.cache"

# Zsh related config file.
export ZDOTDIR="$XDG_CONFIG_HOME/zsh"
export HISTFILE="$ZDOTDIR/.zhistory" # History filepath
export HISTSIZE=10000 # Maximum events for internal history
export SAVEHIST=10000 # Maximum events in history file

# Zim related config file.
export ZIM_HOME="$XDG_DATA_HOME/zim"
EOF

source ~/.zshenv

然后配置 Zsh:

1
2
sudo chsh -s $(which zsh)
git clone https://github.com/LittleNewton/zsh-config.git ~/.config/zsh

输入 zsh 创建一个 Zsh 会话,系统会自动安装所有依赖。

随后执行下列命令安装所需的软件:

1
2
3
4
5
6
7
8
9
ltnt_install all

cd $HOME
GITHUB="https://github.com/LittleNewton"
git clone ${GITHUB}/joshuto-config ~/.config/joshuto
git clone ${GITHUB}/lazygit-config ~/.config/lazygit
git clone ${GITHUB}/tmux-config ~/.config/tmux
git clone ${GITHUB}/tmux-powerline-config ~/.config/tmux-powerline
git clone ${GITHUB}/btop-config.git ~/.config/btop

随后,创建一个 Tmux session,使用快捷键 prefix + I 安装额外插件。

2.1 重点讲解·

  1. 或许系统类型并导出为环境变量。因为 Zsh 脚本需要处理不同的操作系统,当然主要是 macOS 和 Linux,但是为了泛化脚本的可用性,这一步不可避免。
  2. 初始化一些环境变量。比如 TrueNAS 依赖 ${HOME}/bin 里的脚本做存储管理。
  3. 设置别名。常见的命令,比如 iproute2 系列,有别名会更加方便。
  4. Zsh 插件的初始化。先安装 Oh-My-Zsh,然后初始化 Zim,随后初始化 Oh-My-Zsh. 这个顺序不能乱。该结果由实际测试得来,具体原因未知。
  5. 按键映射
  6. conda 环境变量映射 (os-type)
  7. latex 环境变量映射 (os-type)
  8. os-update 函数定义 (os-type)
  9. Kubernetes 补全 (os-type)

总结·

本文以 Debian 12 为例,展示了如何配置 ~/.zshrc,并提高自己日常使用 Linux 的效率。Linux 的高级境界就是给自己写脚本,比如:

  • 写 Linux 脚本以简化系统级工作流程
  • 写 VimScript / VimLua 脚本简化、加速文本编辑、程序调试速度

数字时代,开发能力才是决定性的技能。