理解和配置 Zsh
我们日复一日地重复着许多无聊的任务:创建、复制、移动或搜索文件,一遍又一遍地启动相同的工具、docker 容器等等。
对于开发者来说,shell 是一项宝贵的资产,它可以随着时间的推移提高你的效率。它不仅能为你提供强大的工具,更重要的是,它还能让你实现工作流程中许多部分的自动化。
要利用这些功能,你需要一个强大而灵活的 shell。今天,我想介绍你的下一个好朋友:Z shell,简称 Zsh。
如果你看一下 Zsh 的文档(PDF 版本大约 450 页),你可能会觉得它令人望而生畏。可用的选项太多了,很难想出一个可以作为基础进行构建的基本配置。
在本文中,我们将构建一个基本的 Zsh 配置。我将解释(几乎)所有内容的含义,包括:
- 什么是 Unix shell。
- 为什么 Zsh 是一个不错的选择。
- 如何安装 Zsh。
- 简要概述:
- 有用的环境变量。
- 别名。
- Zsh 选项。
- Zsh 补全。
- Zsh 提示符。
- Zsh 目录堆栈。
- 如何配置 Zsh 使其类似 Vim。
- 如何向 Zsh 添加外部插件。
- 您可以使用外部程序来改善您的 Zsh 体验。
你的键盘准备好了吗?你的手指暖和了吗?你的手臂伸展了吗?那么,开始吧!
Unix Shell 简要概述
Shell解释命令行。您可以在交互式 Shell中使用提示符输入命令行,也可以使用非交互式 Shell运行 Shell 脚本。
Shell 会在您以用户身份登录后立即运行。您可以将 Shell 想象为基于 Unix 的操作系统(包括 Linux)内核的上层。以下是魅力十足的Brian Kernighan 的讲解,他双脚搁在桌子上。
使用图形界面 (GUI) 时,你可以使用鼠标点击来执行任务。而使用 Shell 时,则使用纯文本。
如果您使用图形界面(例如 Windows 管理器或桌面环境),则需要一个终端仿真器来访问 Shell。在过去,终端是一个真实的设备。而现在,它是一个程序。
Shell 可以让你访问许多强大的程序。它们被称为 CLI,即命令行界面。
此时,您可能会想:为什么要使用 shell,而不是图形界面?
- 图形界面设计得不好,尤其是在软件功能繁多的情况下。构建 CLI 可以更简单,避免一些复杂性。
- CLI 通常更快。
- 开发人员经常处理纯文本。CLI 非常适合这种情况。
- 许多 shell(例如 Linux shell)允许您将 CLI 连接在一起以创建强大的转换流程。
- 自动执行文本命令比图形界面上的操作更容易。
尝试一下你的命令 shell,你会惊讶地发现它能让你的工作效率大大提高。
《程序员修炼之道》
Shell 是无鼠标开发环境的基石,也是开发人员可以使用的最强大的工具。
我目前正在写一本书来构建您自己的无鼠标开发环境,其中我解释了如何结合强大的工具来改进和定制您的开发工作流程。
为什么选择 Zsh?
还有其他 Linux shell 可用,包括著名的Bash。为什么要使用 Zsh?
- Zsh 的灵活性和定制化程度令人难以置信。
- 您可以使用您最喜欢的 CLI 的强大自动完成功能。
- Vi 模式对于每个 Vim 爱好者来说都是黄金。
- Zsh 周围有一个重要且活跃的社区。
- Bash 脚本(大部分)与 Zsh 兼容。
有框架还是无框架?
你会看到很多人建议你安装一个 Zsh 框架,里面有大量的插件、选项、别名,而且都已经配置好了。比较著名的有Oh My Zsh和prezto。
我尝试这种方法多年,我认为其弊大于利:
- 我对这些框架都有什么功能一无所知。即使阅读它们的文档,我也记不住所有设置。因此,我几乎用不到 10% 的功能。
- Zsh 已经具有许多功能和选项,在其上建立一个框架就更加令人生畏了。
- 框架是一个很大的外部依赖,它带来了更大的复杂性。如果与我自己的配置发生冲突或出现错误,可能需要很长时间才能弄清楚发生了什么。
- 框架强加的规则和做事方式并不是我想要或需要的。
别误会我的意思:这些框架真是太棒了。它们或许能给你的配置带来一些灵感。但我不会直接用它们。
派对开始吧
我们现在来配置 Zsh。如果我提到的文件或文件夹不存在,你需要创建它们。
此配置已在 Linux 系统上测试过。我对 macOS 系统不太了解,但应该可以正常工作。
安装 Zsh
您可以像其他任何东西一样安装 Zsh:
- Debian / Ubuntu:
sudo apt install zsh
- 红帽:
sudo yum install zsh
- Arch Linux:
sudo pacman -S zsh
- macOS(使用 brew):
brew install zsh
然后,通过输入在终端中运行它zsh
。
Zsh 配置文件
要为用户会话配置 Zsh,您可以使用以下文件:
$ZDOTDIR/.zshenv
$ZDOTDIR/.zprofile
$ZDOTDIR/.zshrc
$ZDOTDIR/.zlogin
$ZDOTDIR/.zlogout
如果你想知道它$ZDOTDIR
代表什么,我们很快会再讨论它。
Zsh 按以下顺序读取这些文件:
.zshenv
- 应该只包含用户的环境变量。.zprofile
- 可用于登录后立即执行命令。.zshrc
- 应用于 shell 配置和执行命令。.zlogin
— 目的相同.zprofile
,但之后再读.zshrc
。.zlogout
- 可用于在 shell 退出时执行命令。
在本文中我们将仅使用.zshenv
和。.zshrc
Zsh 配置路径
默认情况下,Zsh 会尝试在目录中查找用户的配置文件$HOME
。你可以通过设置环境变量来更改它$ZDOTDIR
。
我个人喜欢将所有配置文件放在 中$HOME/.config
。具体做法如下:
- 我将变量设置
$XDG_CONFIG_HOME
如下:export XDG_CONFIG_HOME="$HOME/.config"
。 - 我设置了环境变量
$ZDOTDIR
:export $ZDOTDIR="$XDG_CONFIG_HOME/zsh"
。 - 我把文件放在目录
.zshrc
中$ZDOTDIR
。
大多数软件会使用该路径来$XDG_CONFIG_HOME
安装它们自己的配置文件。因此,您将拥有一个干净的$HOME
目录。
不幸的是,该文件.zshenv
需要位于你的主目录中。你将在那里设置$ZDOTDIR
。然后,之后读取的每个文件都.zshenv
可以进入你的$ZDOTDIR
目录。
Zsh 基本配置
环境变量
正如我们所见,您可以在文件中设置用户会话所需的环境变量$HOME/.zshenv
。该文件应该仅定义环境变量。
例如,您可以在那里设置XDG 基础目录,如上所示:
export XDG_CONFIG_HOME="$HOME/.config"
export XDG_DATA_HOME="$XDG_CONFIG_HOME/local/share"
export XDG_CACHE_HOME="$XDG_CONFIG_HOME/cache"
您还可以确保任何需要文本编辑器的程序都使用您最喜欢的文本编辑器:
export EDITOR="nvim"
export VISUAL="nvim"
您也可以设置一些 Zsh 环境变量:
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
我已经解释过第一行了。对于其他的,它们将会:
- 将您的命令行历史记录存储在文件中
.zhistory
。 - 允许您最多拥有 10000 个条目的历史记录。
这是我的 .zshenv 文件,如果你需要一些灵感。
别名
别名对于提高效率至关重要。例如,我经常使用的 git 别名有很多。越短的别名输入起来就越容易:
alias gs='git status'
alias ga='git add'
alias gp='git push'
alias gpo='git push origin'
alias gtd='git tag --delete'
alias gtdr='git tag --delete origin'
alias gr='git branch -r'
alias gplo='git pull origin'
alias gb='git branch '
alias gc='git commit'
alias gd='git diff'
alias gco='git checkout '
alias gl='git log'
alias gr='git remote'
alias grs='git remote show'
alias glo='git log --pretty="oneline"'
alias glol='git log --graph --oneline --decorate'
我喜欢将我的别名放在一个单独的文件中(令人惊讶的是,名为aliases
),然后将其放在我的.zshrc
:
`source /path/to/my/aliases`
这是我的所有别名。
Zsh 选项
你可以使用或来设置或取消许多Zsh 选项。例如:setopt
unsetopt
setopt HIST_SAVE_NO_DUPS # Do not write a duplicate event to the history file.
unsetopt HIST_SAVE_NO_DUPS # Write a duplicate event to the history file
仅使用这些选项您就能够进行大量自定义。
Zsh 补全系统
与其他 shell 相比,Zsh 的完成系统是其最大的优势之一。
要初始化当前 Zsh 会话的补全,你需要调用函数compinit
。更准确地说,你需要在 中添加以下内容zshrc
:
autoload -Uz compinit; compinit
这是什么意思?
该命令加载一个包含 shell 命令的文件。为了找到此文件,Zsh 将在变量 中定义的Zsh 文件搜索路径autoload
的目录中查找名为 的文件。$fpath
compinit
找到后,其内容将作为函数compinit
加载。函数名即为文件的名称。然后,您可以像调用其他 Shell 函数一样调用此函数。
为什么要使用自动加载,而不是通过执行来获取文件source ~/path/of/compinit
?
- 如果您有一个同名的可执行文件,它可以避免名称冲突。
- 由于该选项,它不会扩展别名
-U
。 - 它仅在需要时加载函数(延迟加载)。这有助于加快 Zsh 的启动速度。
这个-z
选项告诉 Zsh,你的函数是用“Zsh 风格”编写的。我不太确定“Zsh 风格”是什么,但它是自动加载函数的一种惯用方法。
然后,让我们添加以下内容;
_comp_options+=(globdots) # With hidden files
source /my/path/to/zsh/completion.zsh
第一行将自动完成dotfiles。
第二行代码来自 Zsh 框架prezto中的这个文件。我基本上删掉了所有不需要的部分。原始文件在这里。
如果你打开这个补全文件,你会看到很多命令的实例zstyle
。它可以用于定义和查找 Zsh 的样式。你可以尝试一下,了解它的作用。
现在,自动完成功能应该可以工作了:
- 如果您输入
cp
并按下 Tab 键,您将看到 Zsh 将自动完成该命令。 - 如果您键入
cp -
并按下 Tab 键,Zsh 将显示该命令的可能参数。
改进我的 Zsh 提示符
如果没有一个好的提示符,shell 的体验会是什么样的?枯燥、无味、令人沮丧。
说实话:Zsh 默认提示符太丑了。我们必须改掉它,不然眼睛都要哭出来了。我的需求很简单:
- 提示符需要一行。我两行显示有问题。
- 提示符需要在必要时显示一些 git 信息。
从那里,我从另一个提示符创建了自己的提示符。它看起来像这样:
如果你打开提示脚本,你会发现它非常简单:
- 我设置了两个环境变量:
$PROMPT
和$RPROMPT
。第一个格式化左侧提示符,第二个在最右侧显示 git 信息。 - 您可以添加一些格式样式,例如
%F{blue}%f
更改颜色或%Bmy-cool-prompt%b
使所有内容加粗。
此提示不需要任何外部依赖。您可以立即复制并根据需要进行修改。
要加载提示,您需要在中添加类似这样的内容zshrc
:
fpath=(/my/path/to/zsh/prompt $fpath)
autoload -Uz name_of_the_prompt_file; name_of_the_prompt_file
第一行会将包含提示符的文件夹添加到$fpath
,如上所述。它还将确保该文件夹中声明的任何函数/my/path/to/zsh/prompt
都会覆盖其他文件夹中所有同名的函数fpath
。
第二行自动加载提示本身。
此提示要求使用Font Awesome 4来制作 git 图标。您可以下载并安装该字体,也可以更改图标。
Zsh 目录堆栈
Zsh 有在目录堆栈中推送和弹出目录的命令。如果你不知道的话,Bash 也有。
通过操作此堆栈,您可以设置访问过的目录的历史记录,并能够跳回这些目录。
首先,让我们在您的中设置一些选项.zshrc
:
setopt AUTO_PUSHD # Push the current directory visited on the stack.
setopt PUSHD_IGNORE_DUPS # Do not store duplicates in the stack.
setopt PUSHD_SILENT # Do not print the directory stack after pushd or popd.
然后,您可以创建这些别名:
alias d='dirs -v'
for index ({1..9}) alias "$index"="cd +${index}"; unset index
它起什么作用?
- 每个访问的目录都会填充堆栈。
- 当您使用别名时
d
,它将显示堆栈上以数字为前缀的目录。 - 该行将
for index ({1..9}) alias "$index"="cd +${index}"; unset index
创建从 1 到 9 的别名。它们将允许您直接跳转到堆栈上的任何目录。
例如,如果您在 Zsh 中执行,您将跳转到堆栈列表中1
以 为前缀的目录。1
您也可以增加到index ({1..9})
例如index ({1..100})
,如果您希望能够跳回 100 个目录。
它看起来是这样的:
默认使用 Zsh
当您心理上准备好将 Zsh 设置为默认 shell 时,您可以运行以下命令:
- 对于 Linux:
chsh -s $(which zsh)
- 对于 macOS:
sudo sh -c "echo $(which zsh) >> /etc/shells" && chsh -s $(which zsh)
Zsh 已经成为你生活的一部分了。恭喜!
具有 Vim 风格的 Zsh
就编辑而言,Vim 是我的好朋友。我喜欢命令行界面 (CLI) 使用 Vim 的按键绑定,而 Zsh 提供的按键绑定甚至比这更多。
激活 Vi 模式
Zsh 有一个 Vi 模式,您可以通过在中添加以下内容来启用它.zshrc
:
bindkey -v
export KEYTIMEOUT=1
现在,您可以使用 在 INSERT 模式和 NORMAL 模式(也称为 COMMAND 模式)之间切换esc
。为了清晰起见,我在这里用大写字母表示不同的模式,但并非必须如此。
第二行export KEYTIMEOUT=1
使得模式之间的切换更快。
改变光标
一个显示当前模式(NORMAL 或 INSERT)的视觉指示器会很不错。在 Vim 中,|
当我处于 INSERT 模式时,我的光标是横线状的,而█
当我处于 NORMAL 模式时,光标是方块状的。我希望 Zsh 也能有同样的效果。
您可以在您的中添加以下内容zshrc
,或者从文件自动加载它,就像我做的那样。
cursor_mode() {
# See https://ttssh2.osdn.jp/manual/4/en/usage/tips/vim.html for cursor shapes
cursor_block='\e[2 q'
cursor_beam='\e[6 q'
function zle-keymap-select {
if [[ ${KEYMAP} == vicmd ]] ||
[[ $1 = 'block' ]]; then
echo -ne $cursor_block
elif [[ ${KEYMAP} == main ]] ||
[[ ${KEYMAP} == viins ]] ||
[[ ${KEYMAP} = '' ]] ||
[[ $1 = 'beam' ]]; then
echo -ne $cursor_beam
fi
}
zle-line-init() {
echo -ne $cursor_beam
}
zle -N zle-keymap-select
zle -N zle-line-init
}
cursor_mode
现在您可以充满激情和活力地谈论平衡木和滑车了。
Vim 补全映射
为了让 Zsh 更具 Vim 风格,我们可以设置按键hjkl
来导航自动完成菜单。
首先,将以下内容添加到您的zshrc
:
zmodload zsh/complist
bindkey -M menuselect 'h' vi-backward-char
bindkey -M menuselect 'k' vi-up-line-or-history
bindkey -M menuselect 'l' vi-forward-char
bindkey -M menuselect 'j' vi-down-line-or-history
我们在这里加载 Zsh 模块complist
。模块的功能不属于 Zsh 的核心,但可以根据需要加载。有许多不同的模块可供选择。
在这里,该模块complist
允许您访问menuselect
,以自定义移动完成光标的方式。
该命令bindkey -M
将一个键绑定到该命令menuselect
。
在 Vim 中编辑命令行
您也可以使用您最喜欢的编辑器来编辑 Zsh 命令:
autoload -Uz edit-command-line
zle -N edit-command-line
bindkey -M vicmd v edit-command-line
这里,我们引入了 autoloadedit-command-line
函数,这是zshcontrib的一个函数,zshcontrib 是 Zsh 用户贡献的大熔炉。
这个功能edit-command-line
让你可以在文本编辑器中编辑命令行。太棒了!这正是我们想要的。
我们已经看到了bindkey -M
。如果添加vicmd
它,则该按键仅在 NORMAL 模式(在 Zsh 文档中称为 COMMAND 模式)下有效。您可以在此处找到所有可能的 Zsh 模式(键盘映射)。
Zsh 插件
我使用的“插件”一词并非官方说法。人们通常将 Zsh 插件称为可以添加到您自己的配置中的外部配置。
Zsh 有很多这样的插件。它们很多都是 Zsh 框架的一部分。
Zsh 补全
默认情况下,Zsh 可以自动完成许多流行的 CLI cd
,如cp
、、git
等等。
zsh-completions插件添加了更多自动补全功能。新支持的 CLI 列表如下。
如果您不使用列出的任何程序,则不需要此插件。
我将其zsh-completion
作为git 子模块添加到我的 dotfiles 中fpath
。然后,你就可以在 zshrc 中自动将所有自动完成功能添加到, 中:
fpath=(/path/to/my/zsh/plugins/zsh-completions/src $fpath)
您无需逐个加载每个补全文件。如果您查看其中一个文件的开头,您会看到compdef
。这是 Zsh 的一个函数,它会在需要时自动加载补全。补全文件本身只需包含在您的 中即可fpath
。
您也可以挑选您想要的具体完成。
Zsh 语法高亮
Zsh 中的语法高亮怎么样?这就是zsh-syntax-highlighting 的作用所在。
您可以直接获取它:
source /path/to/my/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
你应该在你的 底部引用这个插件zshrc
。这样,之前加载的所有内容都可以在需要时使用语法高亮。
使用 bd 跳转到父目录
你喜欢输入内容cd ../../..
来返回当前文件夹的曾祖父吗?
我也不。
这时bd就能帮到你了。假设你现在在文件夹 中,你可以使用命令~/a/b/c/d
直接跳转到。a
bd a
甚至还包含了 Zsh 的自动补全功能。太棒了!
要使用它,您需要获取文件bd.zsh。
我在配置文件中创建了一个bd文件,该文件指向该文件bd.zsh
。然后,我可以在:bd
中自动加载。这与 Zsh 语法高亮使用的技术相同。zshrc
autoload -Uz bd; bd
自定义脚本
使用 Shell 可以让你通过 Shell 脚本自动化工作流程的许多部分。这是一个巨大的优势,你应该好好利用它。
我将大部分脚本保存在一个文件中,并(粗略地)记录它们,以便我记住其中的内容,并让其他人获得启发。
我在我的源函数中.zshrc
,但你也可以自动加载它们。
工作时,问问自己哪些任务是重复做的,并尽可能地让它们自动化。这才是 Shell 真正的威力所在,它会让你的整个工作流程更加有趣。
外部程序
没有命令行界面的 Shell 毫无用处。以下是我个人最喜欢的扩展 Zsh 功能的命令。
使用 tmux 复用你的 Zsh
我已经在这里写过关于 tmux 的文章了。它是一个具有大量功能的终端多路复用器。
它允许你将终端拆分到不同的 Shell 和不同的窗口中,并进行同步等等。你甚至可以使用插件来扩展它,从而实现 tmux 的自动化。
使用 fzf 进行模糊搜索
模糊查找器fzf
是一款快速而强大的工具。你可以用它来搜索任何你想要的内容,例如文件、命令行历史记录中的条目,甚至是特定的 git 提交信息。
我也使用 zsh编写(或复制粘贴)了一堆脚本,以搜索 git 日志或tmuxp
项目。
安装 有多种方法fzf
。首先你需要一个可执行文件。然后,我建议你获取以下文件:
key-bindings.zsh
,其中包括一些实用的按键,例如Ctrl-h
或Ctrl-t
completion.zsh
,用于fzf
自动完成。
如果您使用 Arch Linux,则需要安装该软件包fzf
并在您的目录中简单地获取这两个文件zshrc
:
source /usr/share/fzf/completion.zsh
source /usr/share/fzf/key-bindings.zsh
否则,您需要按照 fzf 的 README 文件中的安装过程进行操作。
Z-Shell 现已为您准备好
您现在应该有一个干净、精简的 Zsh 配置,并且您应该对它有足够的了解,可以对其进行自定义。
我们从这篇文章中学到了什么?
- Zsh 按照精确的顺序读取其配置文件。
- 您可以根据需要设置(或取消设置)许多 Zsh 选项。
- Zsh 的完成系统是其最好的功能之一。
- Zsh 目录堆栈允许您轻松跳转到您已经访问过的目录。
- 如果您喜欢 Vim,Zsh 允许您使用 Vim 世界中的按键。您甚至可以直接在 Vim 中编辑命令。
- 可以在互联网上找到外部插件,以进一步改善 Zsh 体验。
- 您应该疯狂地编写 shell 脚本,以尽可能地自动化您的工作流程。
- 外部程序可以增强您使用 shell 的体验,例如
tmux
或fzf
。
你所有的同事都会羡慕不已。保证。