介绍
zsh
,也被称为Z壳,由Paul Falstad于1990年创建,为Bourne壳提供替代品。zsh
提供了许多在其他壳中不可用的定制选项,使Z壳成为sh
和bash
的功能丰富的替代品,以及更少使用的壳,如Korn壳(ksh
)和C壳(csh
)。
本文将涵盖由zsh
提供的功能的样本,包括使用zsh
模拟其他壳,使用zsh
内置功能编辑命令行,并使用外部工具vidir
和vipe
,使用zsh
命令中的常规表达式,并设置夹克来自动化壳行为。
前提条件
在开始使用本文中的代码之前,请确保您在系统上安装了终端模拟器,而您的zsh – 版本
是5.8.1
或更高版本。
如果你正在使用Linux,你的zsh
版本低于5.8.1
,你可以使用系统包管理器进行更新,例如,在Ubuntu上使用apt
将安装zsh
版本5.9
。
macOS 用户需要安装一个更新版本的zsh
,这可以通过homebrew
工具完成:
1brew update && brew install zsh
要使用vidir
和vipe
,您必须在您的计算机上安装moreutils
包. 此包可用于 Linux 通过 joeyh.name/code/moreutils/,或与您的 Debian、Ubuntu 和 Arch 上的包管理器一起安装。
最后,本文假设您熟悉命令行工具,以及在终端中编辑文件。
现在,让我们开始吧!
与其他壳相容
虽然zsh
是几种流行的命令行壳之一,但zsh
的基本用途与其他壳不太不同,此外,zsh
还包括与其他壳的某些兼容性选项,因为你喜欢其他壳提供的特定选项,或者你第一次测试zsh
。
zsh
提供的工具来模仿其他壳的行为分为两类:
- 使用壳选项(
setopt
) - 使用
emulate
Builtin
让我们来看看你可以使用这些命令,从setopt
开始。
1、使用setopt
的 Shell 选项
setopt
工具为您定制壳提供了许多选项,但对于本文的目的,我们只将涵盖最有用的选项,让您的zsh
体验在来自另一个环境时变得更舒适。
但是首先,你怎么知道你的壳中有哪些选项是活跃的呢?在没有论点的情况下运行setopt
会显示你已启用的选项列表,虽然这很有帮助,但你可能需要做一些挖掘来找到zsh
壳中可用的选项的完整列表(https://zsh.sourceforge.io/Doc/Release/Options.html)。
ksh_option_印刷
ksh_option_print
选项允许您更改用 setopt
打印的选项列表,从显示仅有启用选项到显示每个选项和相应的打开
或关闭
列。
1# setopt kshoptionprint will also work
2setopt ksh_option_print
以下是从setopt
的选项的简化列表,每个选项都有启用/关闭指标。
1setopt
1noaliases off
2allexport on
3noalwayslastprompt off
4alwaystoend on
5appendcreate off
6noappendhistory off
7autocd on
8autocontinue off
9noautolist off
10noautomenu off
11autonamedirs off
12noautoparamkeys off
13noautoparamslash off
14autopushd on
15noautoremoveslash off
16autoresume off
17nobgnice off
18braceccl off
19bsdecho on
bsd_echo 对话
默认情况下,zsh 会将 newline (\n) 或 tab (\t') 字符打印为非可见,但可能有时您需要在字符串中具有字面 backslash escaped 字符。
1setopt bsd_echo
2# prints tab and newline characters
3echo "\t\n"
4
5unsetopt bsd_echo
6
7# will print the literal "\t\n"
8echo "\t\n"
csh_junkie_路由器
csh_junkie_loops
选项为在列表或数组上循环提供了更为简单的语法。启用此选项允许您在循环中的每个项目上执行命令之前使用循环,而无需使用Do
关键字。
虽然这可能看起来不像是一个巨大的改进,但缩短的语法可以帮助您随着时间的推移更快地与壳进行交互,通过消除记住创建zsh
中的语法正确循环所需的关键字的负担。
下面的例子:
1# the for command requires "do" before any actions
2# can be performed on the list of items
3# a traditional zsh loop
4for f in 1 3 4 5; do print $f; done
使用csh_junkie_loops
选项启用,您可以使用更短的语法来实现相同的结果:
1setopt csh_junkie_loops
2for f in 1 3 4 5; echo $f; end
3$ 1
43
54
65
csh_nullcmd 可用
在清除终端中的文件内容时,您可以使用类似>file.txt
的命令,该命令将重定向一个空串,以取代文件的内容. 如果您正在处理敏感数据,或者您想避免用空数据重写文件,您可以激活csh_nullcmd
选项,以防止没有先前的命令发生文件重定向。
激活此选项后,不遵循命令的任何重定向都会导致错误。
1setopt csh_nullcmd
2
3# attempting to overwrite file.txt will create an error
4>file.txt
1zsh: redirection with no command
您可以通过在终端中输入unsetopt chs_nullcmd
来禁用此选项。
在zsh
中,数组是一个索引的,这意味着您必须使用$array[1]
来获取第一个项目,这种行为对那些习惯于使用包含零索引数组的编程语言的开发者来说是反直观的。
ksh
中的数组允许您使用零索引,模仿许多编程语言的行为. 此选项还要求您使用弯曲的轴承来访问数组元素,而不是使用平方轴承,就像默认的zsh
行为一样,弯曲的轴承是可选的。
1declare -a kitchen_items=(plates cutlery oven sink)
2print ${kitchen_items[1]}
3# plates
使用 ksh_arrays
选项集, $kitchen_items[1]
引用cutlery
而不是plates
1setopt ksh_arrays
2declare -a kitchen_items=(plates cutlery oven sink)
3
4print ${kitchen_items[1]}
5# cutlery
sh_word_split 解析
默认情况下,zsh 不会将多单词变量分成单个元素,如sh
和ksh
壳,这意味着包含三个单词的变量将被视为单个元素,而不是三个单独单词。
1kitchen_items="plates cutlery oven sink"
2
3# without sh_word_split
4for word in $kitchen_items; do
5 print "$word"
6end
要强迫zsh
通过在间隙中划分模仿sh
的行为,请使用setopt sh_word_split
1setopt sh_word_split
现在,让我们用略有不同的语法打印$kitchen_items
的内容:
1kitchen_items="plates cutlery oven sink"
2for word in $kitchen_items[@]; do
3 print "$word"
4end
5# the loop above will print the following text:
6# plates
7# cutlery
8# oven
9# sink
上面的示例可以在您的壳中使用,或者为了更永久地使用setopt
,您可以将上述任何setopt
命令添加到您的.zshrc
文件中,以便在任何交互式壳会话中提供这些选项。
但是,如果你发现自己需要一种不同的方式来模仿其他壳的行为,让我们看看模拟
命令。
2、使用模拟
命令
要模拟在zsh
中另一个壳,您可以使用模拟 builtin 命令. 例如,要模拟 Bash 壳,您可以使用以下命令:
1emulate bash
这将使zsh
在大多数方面表现得像Bash壳一样,然后您可以在zsh
中运行Bash脚本和命令,并且它们应按预期工作。
要模拟其他壳,例如Bourne或Korn壳,您可以在模拟命令中使用相应的名称而不是bash,例如,要模拟Korn壳,您可以使用以下命令:
1emulate ksh
如果你有一个命令,你想在sh
模拟模式中使用,你可以使用
1emulate sh
一旦设置,您可以通过在终端中键入模拟
来打印当前模拟模式。
模拟
命令还允许您使用c
选项将粘贴
模拟应用于函数,这意味着模拟命令将在该函数后执行的任何命令的范围内,在执行模拟
命令后。
1function hello_world_sh () {
2 emulate sh -c "print hello world";
3}
模拟sh -c
命令可用于创建需要POSIX兼容的函数,同时仍然利用zsh
环境。
在飞机上编辑
在日常工作中,您可能需要编辑许多类型的文件 - 您正在构建的应用程序的配置,或者如果您只是从任务列表中检查一个项目。
有几个工具可用于zsh
,这将允许您无缝地编辑zsh
内部环境的各个方面,从交互式终端的核心,zsh Line Editor开始。
zsh 线程编辑器
zsh 行编辑器,或 ZLE 是您与zsh
一起使用的编辑器,用于输入和修改终端中的命令行上的命令.所有壳都有类似的行编辑器,在大多数情况下您不需要修改其行为。
ZLE在加载zsh
时会自动激活,但它也存在于一个名为zsh/ZLE
的zsh
模块。
在zsh
中,ZLE(zsh Line Editor)是内置的行编辑接口,允许您编辑命令行并导航命令历史。
如果您在交互式壳中使用zsh
,则默认情况下启用了ZLE,因为它是zsh
交互式功能的核心。
1# This command will print "ZLE: on" if the zsh Line Editor is enabled.
2[[ -o zle ]] && echo "zle: on"
直接与 ZLE 交互的主要用途是通过为您的交互性壳环境设置默认的关键链接,这些选项可以允许您使用特定于您喜爱的编辑器的关键链接,无论是emacs
还是vi
。
如果您更喜欢 emacs 密钥绑定,请在终端中输入以下命令。
1bindkey -e
要启用 vi keybindings,请使用以下命令:
1bindkey -v
现在你已经了解了一点关于 zsh 行编辑器,让我们看看你如何使用它与zed
编辑器。
西德
「Zed」使用ZLE,允许您编辑命令行上的脚本和功能,而无需打开编辑器。这对于您导航文件系统时快速编辑非常好,但也可以用作任何一般编辑命令的基本编辑器。
要开始,在您的终端中键入zed
,以检查它已安装在您的系统上。
1zed
如果zed
命令不可用,则可以从 https://github.com/zsh-users/zsh/blob/master/Functions/Misc/zed获取代码并将其保存为可执行的文件,或将其包装到要源的函数中。
一旦在您的环境中设置了zed
,在您的终端中键入zed
将创建一个新的,您可以根据需要编辑命令行。
若要在你的zsh
环境中编辑特定的函数,你可以使用zed -f
。
1zed -f precmd
请注意,用zed
对您的函数所做的更改只会存在,直到您通过source.zshrc
或类似的命令重新加载配置文件。
变种
「vared」允许您使用您偏好的文本编辑器修改变量的值,而不是使用设置或导出命令手动在命令行上设置该值. 要使用「vared」命令,您首先需要指定您想要编辑的变量的名称。
1vared EDITOR
这将使用ZLE
打开EDITOR
环境变量的值,在那里您可以根据需要编辑该值。
用Vidir
编辑目录名称
Vidir
允许您在默认编辑器中编辑目录(或文件)的名称,这有助于快速视觉地修改目录名称,而不是依靠模式匹配或循环。
1vidir "$HOME"
Vidir
将创建一个包含您的$HOME
目录中的每个项目的名称的临时文件.在您更改并保存文件后,vidir
将更新您修改的任何文件或文件夹的名称。
您还可以使用Vidir
更改单个文件的名称,通过将您选择的文件名转移到vidir
作为参数而不是目录。
1vidir /path/to/file.txt
在您的默认编辑器中打开file.txt
,您可以随意修改文件名。
使用Vipe在管道中的编辑器
有时在默认编辑器中修改管道的内容有用,而不是依赖awk
或sed
等工具来执行管道内的文本操作。vipe
命令可以插入任何管道,并允许您视觉地编辑以前的命令的结果。
下面的示例使用vipe
来修改前面的curl
命令的内容,然后将结果保存到名为example_modified.txt
的文件中。
1curl "https://example.com" | vipe > example_modified.txt
常规表达
「zsh」提供了一套广泛的 glob 运算符,一种形式的 扩展,如果你来自一个更基本的壳,这可能是压倒性的。
我倾向于通过使用noglob
壳选项完全关闭我的环境,但这不需要:
1setopt noglob
雷克斯在测试块
zsh
提供了通过 =~
比较运算器的基本常规表达式匹配。这允许您在执行 if
块时使用常规表达式匹配文本。
1variable="file.txt"
2
3if [[ "$variable" =~ (txt|zsh) ]];
4 then
5 print "variable contains txt or zsh";
6 else
7 print "variable does not contain txt or zsh"
8fi
使用 zsh/regex
该 zsh/regex 模块
允许您使用旗帜在测试条件下启用正规表达式. 该旗帜使用 POSIX 扩展正规表达式来匹配文本部分。
要将zsh
模块加载到您的环境中,请使用zmodload
命令:
1zmodload zsh/regex
一旦加载,您可以在测试块中使用-regex-match
标志来应用常规表达式,例如,您可以从上面的示例中缩短if
块到test
一行命令。
1zmodload zsh/regex
2
3test "txt" -regex-match (txt|zsh) && echo "contains txt or zsh" || print "does not contain txt or zsh"
您可以通过打印MATCH
环境变量来检查特定匹配。
1print $MATCH
2
3txt
使用zsh/PCRE
与zsh/regex
一样,您可以通过zmodload
命令加载zsh/prce
模块:
1# note: pcre must be lowercase for the module to load
2zmodload zsh/pcre
它包含三个命令:pcre_compile、pcre_study、pcre_match。
- pcre_compile 编译一个兼容 perl 的常规表达式。
- 此命令的选项匹配 PCREs 的选项
- pcre_match 一个独立的命令来匹配字符串与 PCREs。
1pcre_compile -m "bark$"
2string="dog says bark"
3pcre_match -b -- $string
4print $MATCH
此外,zsh/pcre 提供了称为pcre-match
的测试条件,该条件与字符串相匹配,类似于在上述zsh/regex
模块中可用的regex-match
旗帜。
1test "txt" -regex-match (txt|zsh) && print "contains txt or zsh" || print "does not contain txt or zsh"
回复与 Hooks 的事件
zsh
Hooks 是 特殊函数在壳的执行中自动执行的特定点,例如在提示时输入命令或壳启动时。
在zsh
中,chpwd
函数是一个特殊的函数,每当当前工作目录更改时都会自动执行,这允许您每次用户更改目录时执行某些操作或任务。
要使用chpwd
函数,您首先需要在您的.zshrc
文件中定义它. 要定义chpwd
函数,请在配置文件中添加以下行:
1chpwd () {
2 # Your commands and actions go here
3}
您可以使用chpwd
函数更新命令提示中的目录,或显示指示新工作目录的消息。
一旦您在配置文件中定义了chpwd
函数,它每次更改当前工作目录时都会自动执行,您可以通过在zsh
会话中更改目录并观察chpwd
函数的效果来测试它。
期刊
编写一个名为周期函数时,每 n 秒就会运行包含的命令,其中秒数存储在环境变量 $PERIOD
中,例如,假设您想要运行一个函数,该函数每 6 小时下载我的标记。
使用环境变量$PERIOD
来设置函数运行的频率。
1periodic() {
2
3}
预告片
当终端重新绘制时,此操作不会运行。当背景流程向当前提示发送通知时,会发生重新绘制。
作为一个更具体的例子,我的precmd
函数包含一个命令,该命令将当前的目录存储在一个文件中. 这使我能够在最近使用的目录中打开一个新的终端。
1precmd () {
2 pwd >"$HOME/.zsh_reload_directory.txt" &
3}
这将当前的目录附加到我的$HOME
目录中的名为.zsh_reload.txt
的文件中. 要在终端加载时自动导航到此目录,请在您的.zshrc
文件中添加下列命令
1# create the precmd function
2precmd () {
3 pwd >|"$HOME/.zsh_reload_directory.txt" &
4}
5
6# add the navigation command outside of the precmd function
7cd "$HOME/.zsh_reload_directory.txt"
预告片
執行後閱讀命令,但顯示命令提示之前。
「precmd」函数的典型用途是设置「$PS1」命令提示(https://ubuntu.com/tutorials/command-line-for-beginners)变量,包含有用的信息,例如您最后执行命令的时间,但您不限于更新命令提示。
历史
zshaddhistory
是一个特殊的函数,每次输入命令时都会自动执行,这允许您在输入命令时执行某些操作或任务,例如在执行命令之前登录命令或修改命令。
要使用zshaddhistory
,您可以将其定义为您的.zshrc
文件中的函数:
1zshaddhistory () {
2 # Your commands and actions go here
3}
您可以使用zshaddhistory
函数将命令登录到文件中,或在执行之前修改命令。
一旦您在配置文件中定义了zshaddhistory
函数,每次输入命令时都会自动执行,您可以通过在命令中输入命令并观察zshaddhistory
函数的效果来测试它。
添加新胡子
要使用add-zsh-hook
函数,您首先需要定义一个自定义的绑定函数,该函数包含您想要在指定点执行的命令和操作,例如,您可以定义一个绑定函数,该函数记录在提示中输入的命令,如下:
1log_commands () {
2 print "$(date): $1" >> ~/.zsh_command_log
3}
一旦您定义了自定义绑定函数,您可以使用add-zsh-hook
函数将其绑定到现有的zsh
绑定函数中,例如,要将log_command
函数绑定到zshaddhistory
绑定函数中,每次在提示中输入命令时,可以使用以下命令:
1add-zsh-hook zshaddhistory log_commands
这将导致log_command
函数每次输入命令时执行,并将命令登录到~/.zsh_command_log
文件中。
1hooks-define-hook
在zsh
中,用于定义自定义的功能。zsh
是在壳的执行中在特定点自动执行的特殊函数,例如在提示中输入命令时或壳开始时。
若要使用「hooks-defined-hook」函数,您首先需要决定何时和何处要执行自定义的hook。这将确定该hook的名称和参数。例如,如果您希望每次在提示中输入命令时执行你的hook,您可以使用「zshaddhistory」的hook,该命令是唯一的参数。
一旦你已经决定了你的链接的名称和参数,你可以使用链接定义链接
函数来定义链接,例如,如果要定义一个链接,每次输入一个命令,并将该命令登录到一个文件,你可以使用以下命令:
1hooks-define-hook zshaddhistory log_command
这将定义一个名为zshaddhistory
的链接,该链接需要一个单一的论点(在提示中输入的命令)。
一旦您定义了链接,您可以使用add-zsh-hook
函数将函数附加到链接中,此函数将在每次触发链接时执行。
结论
在本文中,您了解了许多方法,您可以通过使用飞行的编辑器来自定义您的工作流程,应用regex来匹配变量和其他文本,并在您的环境中设置链接,以自动响应您的壳中的事件。