跳到主要内容

ZSH自动补全脚本编写

· 阅读需 17 分钟
inhere

告诉 zsh 使用哪个函数来完成命令

命令的完成函数存储在名称以下划线 _ 开头的文件中,这些文件应放置在 $fpath 变量中列出的目录中。 可以通过在 ~/.zshrc 文件中添加这样的行来向 $fpath 添加目录:

fpath=(~/newdir $fpath )

完成函数文件的第一行看起来像这样:

# compdef foobar

这告诉 zsh 该文件包含用于完成 foobar 命令的代码。这是第一行最常使用的格式,但如果需要,您也可以使用相同的文件来完成多个不同的功能。 请参阅此处了解更多详情。

你也可以直接使用 compdef 命令(例如在你的 ~/.zshrc 文件中)告诉 zsh 使用哪个函数来完成这样的命令:

> compdef _function foobar

或者对几个命令使用相同的完成:

> compdef _function foobar goocar hoodar

或者如果你想提供参数:

> compdef ' _function arg1 arg2 ' foobar

请参阅此处了解更多详情。

编写自己的完成函数

一个很好的入门方法是查看一些已经定义的完成函数。在我的 linux 安装中,这些可以在 /usr/share/zsh/functions/Completion/Unix/usr/share/zsh/functions/Completion/Linux 和其他一些子目录中找到。

您会注意到 _arguments 函数在这些文件中被大量使用。这是一个实用函数,可以轻松编写简单的完成函数。_arguments 函数是 compadd 内置函数的包装器。compadd 内置函数是用于向命令行添加完成词并控制其行为的核心函数。 但是,大多数情况下您不需要使用 compadd,因为有许多实用函数,例如 _arguments_describe,它们更易于使用。

对于非常基本的完成,_describe 函数应该足够了

实用功能

以下是一些可能有用的实用函数的列表。实用功能的完整列表以及完整的解释可在此处获得。下一节将给出如何使用这些函数的示例。

整体完成的主要实用功能

函数说明
_alternative可用于从其他实用程序函数或 shell 代码生成完成候选。
_arguments用于指定如何完成带有 unix 样式选项的命令的单个选项和参数。
_describe用于创建由带有描述(但没有动作 ACTION)的单词组成的简单完成。比 _arguments 更容易使用
_gnu_generic可用于完成理解 --help 选项的命令的选项。
_regex_arguments创建一个函数,用于将命令行参数与正则表达式匹配,然后执行操作/完成。

用于执行单个单词的复杂完成的函数

函数说明
_values用于完成任意关键字(值)及其参数,或此类组合的逗号分隔列表。
_combination用于完成值的组合,例如主机名和用户名对。
_multi_parts用于分别完成单词的多个部分,其中每个部分由一些字符分隔,例如用于完成部分文件路径:/u/i/sy -> /usr/include/sys
_sep_parts与 _multi_parts 类似,但允许在完成的不同部分使用不同的分隔符。
_sequence用作另一个完成函数的包装器,以完成由该其他函数生成的分隔匹配列表。

用于完成特定类型对象的函数

函数说明
_path_files用于完成文件路径。采取多种选择来控制行为。
_files使用除 -g-/ 之外的所有选项调用 _path_files。这些选项取决于文件模式样式设置。
_net_interfaces用于补全网络接口名称
_users用于填写用户名
_groups用于完成组名
_options用于补全 shell 选项的名称。
_parameters用于完成 shell 参数/变量的名称(可以限制为匹配模式的那些)

处理缓存完成的函数

如果您有大量完成,您可以将它们保存在缓存文件中,以便快速加载完成。

函数说明
_cache_invalid指示与给定缓存标识符对应的完成缓存是否需要重建
_retrieve_cache从缓存文件中检索完成信息
_store_cache在缓存文件中存储与给定缓存标识符相对应的完成

其他功能

函数说明
_message用于在无法生成完成的地方显示帮助消息。
_regex_words可用于为 _regex_arguments 命令生成参数。这比手动编写参数更容易。
_guard可用于 _arguments 的规范的 ACTION 和类似函数来检查正在完成的单词。

动作 Action

许多实用函数,例如 _arguments _regex_arguments _alternative_values 可能在选项/参数规范的末尾包含一个动作。此动作指示如何完成相应的参数。动作 Action 可以采用以下形式之一:

函数说明
()需要参数,但不会生成匹配项。
(ITEM1 ITEM2)可能匹配项列表
((ITEM1\:'DESC1' ITEM2\:'DESC2'))可能的匹配列表,附带描述信息。注意:Action 中的引号不能和其所在的参数格式字符串的引号相同
->STRING$state设置为字符串并继续($state可在实用功能调用后在案例陈述中检查)
FUNCTION用于生成匹配或执行某些其他操作的函数名称. 例如 _files_message
{EVAL-STRING}将字符串评估为 shell 代码以生成匹配项。这可用于调用带有参数的实用程序函数,例如 _values_describe
=ACTION在完成命令行中插入一个虚拟词而且不会改变补全位置节点

并非所有操作类型都可用于使用它们的所有实用功能。例如,->string 类型在_regex_arguments或_alternative功能中不可用。

使用 _describe 编写简单的完成函数

_describe 函数可用于简单的补全,其中选项/参数的顺序和位置并不重要。您只需要创建一个数组参数来保存选项及其描述,然后将参数名称作为参数传递给 _describe。下面的示例创建完成候选 c 和 d,以及描述(注意这应该放在 $fpath 中列出的某个目录中名为 _cmd 的文件中)。

#compdef cmd
local -a subcmds
subcmds=('c:description for c command' 'd:description for d command')
_describe 'command' subcmds

您可以使用以下由双连字符分隔的多个不同列表,但请注意,这混合了单个标题下的匹配项,并且不打算与不同类型的完成候选者一起使用:

local -a subcmds topics
subcmds=('c:description for c command' 'd:description for d command')
topics=('e:description for e help topic' 'f:description for f help topic')
_describe 'command' subcmds -- topics

如果两个候选人有相同的描述,_describe 将它们收集在同一行中,并确保描述在列中整齐对齐。_describe 函数可以在 ACTION 中用作 _alternative_arguments_regex_arguments 规范的一部分。在这种情况下,您必须将它的参数放在大括号中,例如 'TAG:DESCRIPTION:{_describe 'values' options}'

使用 _alternative 编写完成函数

_describe 一样,此函数执行简单的补全,其中选项/参数的顺序和位置并不重要。然而,与 _describe 不同的是,可以调用其他函数而不是固定匹配来生成完成候选。此外,_alternative 允许混合不同类型的完成候选。

作为参数,它采用 'TAG:DESCRIPTION:ACTION' 形式的规范列表,其中 TAG 是标识完成匹配类型的特殊标签,DESCRIPTION 用作标题来共同描述完成候选组,而 ACTION是前面列出的动作类型之一(除了 ->STRING=ACTION 形式)。例如:

_alternative 'arguments:custom arg:(a b c)' 'files:filename:_files'

第一个规范添加完成候选 a、b 和 c,第二个规范调用 _files 函数来完成文件路径。

我们可以用 \ 将规范分成几行,并为每个自定义参数添加描述,如下所示:

_alternative \
'args:custom arg:((a\:"description a" b\:"description b" c\:"description c"))' \
'files:filename:_files'

如果我们想将参数传递给 _files,它们可以简单地包含在内,如下所示:

_alternative \
'args:custom arg:((a\:"description a" b\:"description b" c\:"description c"))'\
'files:filename:_files -/'

要使用参数扩展来创建我们的完成列表,我们必须使用双引号来引用规范,例如:

_alternative \
"dirs:user directory:($userdirs)" \
"pids:process ID:($(ps -A o pid=))"

在这种情况下,第一个规范添加存储在 $userdirs 变量中的单词,第二个规范评估 'ps -A o pid=' 以获取用作完成候选的 pid 列表。在实践中,我们会为此使用现有的 _pids 函数。

我们可以使用其他实用函数,例如 ACTION 中的 _values 来执行更复杂的完成,例如:

_alternative \
"directories:user directory:($userdirs)" \
'options:comma-separated opt: _values -s , letter a b c'

这将完成 $userdirs 中的项目,以及包含 a, b &/or c 的逗号分隔列表。注意 _values 之前的初始空格的使用。这是必需的,因为 _values 不理解用于描述的标准 compadd 选项。

_describe 一样,_alternative 函数本身可以作为 _arguments_regex_arguments 规范的一部分在 ACTION 中使用。

使用 _arguments 编写完成函数

通过对 _arguments 函数的一次调用,您可以创建相当复杂的完成函数。它旨在处理带有各种选项和一些普通参数的典型命令。与 _alternative 函数一样,_arguments 将规范字符串列表作为参数。这些规范字符串指定选项和任何相应的选项参数(例如 -f 文件名)或命令参数。

基本选项规范采用 '-OPT[DESCRIPTION]' 的形式,例如:

# sort output: 排序输出
# long output: 长输出
_arguments '-s[sort output]' '--l[long output]' '-l[long output]'

可以在 '-OPT[DESCRIPTION]:MESSAGE:ACTION' 形式的选项描述之后指定选项的参数,其中 MESSAGE 是要显示的消息,而 ACTION 可以是上面 ACTIONS 部分中提到的任何形式。例如:

_arguments '-f[input file]:filename:_files'

命令参数规范采用 'N:MESSAGE:ACTION' 形式,其中 N 表示它是第 N 个命令参数,而 MESSAGE & ACTION 和以前一样。如果省略 N,则它仅表示下一个命令参数(在任何已指定的参数之后)。如果在开头(在 N 之后)使用 双冒号,则该参数是可选的。例如:

_arguments '-s[sort output]' '1:first arg:_net_interfaces' '::optional arg:_files' ':next arg:(a b c)'

这里第一个 arg 是网络接口,下一个可选 arg 是文件名,最后一个 arg 可以是 a、b 或 c,并且 -s 选项可以在任何位置完成。

_arguments 函数允许使用上面 ACTION 部分中列出的全套 ACTION 表单。这意味着您可以使用操作来选择 case 语句分支,如下所示:

_arguments '-m[music file]:filename:->files' '-f[flags]:flag:->flags'
case "$state" in
files)
local -a music_files
music_files=( Music/**/*.{mp3,wav,flac,ogg} )
_multi_parts / music_files
;;
flags)
_values -s , 'flags' a b c d e
;;
esac

在这种情况下,音乐文件(music_files)的路径使用 _multi_parts 函数逐步降序完成,标志使用 _values 函数作为逗号分隔的列表完成。

我这里刚刚给你介绍了 _arguments 规范的基础知识,你还可以指定互斥选项、重复选项&参数、以+开头的选项而不是-等,更多细节参见官方文档。 另请查看本文档末尾提到的教程,以及src 目录中的完成功能。

测试与调试

要重新加载完成功能:

> unfunction _func
> autoload -U _func

可以调用以下函数来获取有用的信息。如果默认键绑定不起作用,您可以尝试按 Alt+x,然后输入命令名称。

功能默认键绑定描述
_complete_helpCtrl+xh显示有关在当前光标位置完成时使用的上下文名称、标签和完成函数的信息
_complete_helpAlt+2 Ctrl+xh如上所述,但显示更多信息
_complete_debugCtrl+x ?执行普通完成,但在临时文件中捕获完成系统执行的 shell 命令的跟踪

陷阱(注意事项)

请记住在包含完成函数的文件的开头包含 #compdef 行。

注意对 _arguments_regex_arguments 的规范使用正确的引用类型:如果规范中存在需要扩展的参数,请使用双引号,否则使用单引号,并确保在项目描述周围使用不同的引号。

检查您在 _arguments_alternative_regex_arguments 等规范的正确位置是否有正确数量的 :

记住在使用 _regex_arguments 时包含一个初始模式来匹配命令词(它不需要匹配操作)。

请记住在 _regex_arguments 的任何 PATTERN 参数的末尾放置一个空字符 $'\0'

提示

有时你会遇到这样的情况,在子命令之后只有一个选项,当在子命令之后按下 tab 时,zsh 会自动完成这个。相反,如果您希望在完成之前将其与其描述一起列出,您可以向 ACTION 添加另一个空选项(即 \:),如下所示 ':TAG:DESCRIPTION:((opt1\:”description for opt1” \:))' 请注意这一点仅适用于在其规范参数(_arguments_regex_arguments 等)中使用 ACTION 的实用程序函数

本文翻译自 zsh-users/zsh-completions

TIP: 更完整的翻译文章参看 http://chuquan.me/2020/10/02/zsh-completion-tutorial/