如果我在这里做了一些愚蠢的事情,请原谅我,文档很大,而且搜索还没有发现任何东西。
我正在尝试为名为 的自定义脚本创建 shell 补全fab
。对于 bash 来说很简单,只需将它们放入即可/etc/bash_completion.d
即可工作。但是天哪,zsh 是 PITA 吗?
我有完成功能_fab
,并且在启用时工作正常compdef _fab fab
。我把它放在/usr/share/zsh/vendor-completions/_fab
已经在我的$fpath
.该文件以 开头#compdef fab
并以 结尾compdef _fab fab
。看起来不错:
$ type _fab
_fab is an autoload shell function
但是每当我启动一个新的 shell 时,fab
补全就不起作用(来自 的其他功能vendor-completions
,例如_docker
,都很好)。compinit
修复了该特定 shell 的问题。我发现让它rm ~/.zcompdump ~/.zcompdump-$(hostname)-5.1.1; compinit
永久工作(5.1.1 =我的 zsh 版本)。
问题:
- 什么时候读取什么
~/.zcompdump
来设置初始完成? man zshall
说:下一次调用 compinit 将读取转储的文件,而不是执行完整的初始化。
如果是这样的话,
compinit
在删除之前不会修复我的完成情况~/.zcompdump
,对吧?我错过了什么吗?- 它与什么
~/.zcompdump-$(hostname)-5.1.1
相关以及如何相关.zcompdump
?唯一的区别是其中的一个完成~/.oh-my-zsh/completions
(因为$ZSH
指向~/.oh-my-zsh
) 中的一个完成。这是 oh-my-zsh 的事情吗? - 如果我要将这些补全打包到可再发行包中或创建安装程序脚本,我应该将 zsh 补全放在哪里?在安装过程中我还应该做什么来确保一切正常工作?
我的目标是 Ubuntu 16.04、18.04 和 19.04,但欢迎提供非发行版特定的信息。我正在 Ubuntu 16.04 上使用 zsh 5.1.1 和最近的 oh-my-zsh 进行测试。
答案1
TL,DR:在正常操作中,只需将文件放入相应的目录即可。测试时,您需要删除缓存文件(.zcompdump
默认情况下,但用户可以将其放在不同的位置,oh-my-zsh 确实将其放在不同的位置)。
简单的答案是将完成函数写入第一行所在的文件中#compdef fab
。该文件必须位于 上的目录中$fpath
。
该文件可以包含函数体,也可以包含函数的定义,后跟函数的调用。也就是说,该文件包含类似的内容
#compdef fab
_arguments …
或者
#compdef fab
function _fab {
_arguments …
}
_fab "$@"
$fpath
该文件必须在运行之前存在compinit
。这意味着您需要注意其中的顺序.zshrc
:首先添加任何自定义目录$fpath
,然后调用compinit
。如果您使用 oh-my-zsh 等框架,请确保$fpath
在 oh-my-zsh 代码之前添加任何自定义目录。
compinit
是初始化完成系统的函数。它读取所有文件$fpath
并检查它们的第一行是否有魔术指令#autoload
和#compdef
。
.zcompdump
是一个由compinit
.~/.zcompdump
是默认位置;运行时您可以选择不同的位置compinit
。 Oh-my-zsh 调用compinit
时可以-d
选择使用变量 给出的不同缓存文件名ZSH_COMPDUMP
,这默认为
ZSH_COMPDUMP="${ZDOTDIR:-${HOME}}/.zcompdump-${SHORT_HOST}-${ZSH_VERSION}"
包含主机名是为了那些主目录在计算机之间共享并且可能在不同计算机上安装了不同软件的人。包含 zsh 版本是因为缓存文件在版本之间不兼容(它包含因版本而异的代码)。
我认为你所有的问题都是由于陈旧的缓存文件造成的(这让你的情况变得过于复杂)。很遗憾,zsh判断缓存文件是否过时的算法并不完美,大概是为了速度。它不会检查 上文件的内容或时间戳$fpath
,它只是对它们进行计数。文件.zcompdump
以如下行开头
#files: 858 version: 5.1.1
如果 zsh 版本和文件数量正确,zsh 会加载缓存文件。
缓存文件仅包含命令名称之间的关联,而不包含完成函数的代码。以下是缓存透明工作的一些常见场景:
- 如果将新文件添加到
$fpath
,这会使缓存失效。 - 更一般地,如果您在 上添加和删除文件
$fpath
,并且删除的文件总数与删除的文件总数不同,这会使缓存失效。 - 如果将文件移动到其他目录
$fpath
而不更改其名称,这不会影响缓存中的任何内容,因此缓存保持正确。 - 如果您修改文件
$fpath
而不更改其第一行,则这不会影响缓存中的任何内容,因此缓存保持正确。
以下是一些缓存失效但 zsh 没有意识到的常见场景。
- 您添加一些文件
$fpath
并删除完全相同数量的文件。 - 您将文件重命名为
$fpath
. - 您可以添加或修改文件顶部的
#compdef
(或) 行。#autoload
最后一点是测试过程中最棘手的问题。如果更改该#compdef
行,则需要删除该.zcompdump
文件并重新启动 zsh (或重新运行compinit
)。
如果您将补全放入可再发行包中,只需将补全文件放入系统范围内的目录中$fpath
。对于 Ubuntu 软件包,适当的位置是/usr/share/zsh/vendor-completions
.对于安装在 下的东西/usr/local
,那就是/usr/local/share/zsh/site-functions
。这就是您需要做的全部。
不透明的一件事是您是否需要#compdef
在升级中更改行,或者删除或重命名某些文件。在这种情况下,用户将需要删除其缓存文件,而这不是您可以从安装在多用户计算机上的软件包中执行的操作。