Bash的Tab补全脚本能够在zsh中使用吗?

111

我有一个 Apache Hadoop 的 Bash 补全脚本。通常,我在日常工作中使用 zsh 作为我的 shell。当需要时,它往往像 Bash 一样。但是,在它们之间的 tab 补全系统看起来存在根本性差异。是否有一种简单的方法可以将现有的 bash-tab-completion 定义转换为适用于 zsh 的定义?我不想花费大量时间在此上,但如果很容易的话,我可以节省相当多的精力。

6个回答

184
autoload bashcompinit
bashcompinit
source /path/to/your/bash_completion_file

11
这是更好的答案,因为它告诉你如何使用bashcompinit。做得好。 - pyrospade
6
谢谢,好的回答。不幸的是,如果脚本使用了 _init_completion bash 命令,这种方法就不起作用了。 - 0fnt
6
这对我没有起作用。我仍然收到“complete:13: command not found: compdef”错误提示。 - Jatin Kumar
1
如果它不起作用,请检查脚本(噢:) - 它可能在顶部有一个 if is-bash 检查。 - olejorgenb
1
@FranklinYu,你解决了吗?我也遇到了同样的问题,我的原始问题因此被关闭了:( - user12056260
显示剩余2条评论

56
autoload -U +X compinit && compinit
autoload -U +X bashcompinit && bashcompinit
source /path/to/your/bash_completion_script

我正在运行 zsh zsh 5.0.2 (x86_64-apple-darwin13.0)没有任何 ~/.zshrc,并且上述命令在一个全新的 zsh shell 中可以正常工作。感谢 git-completion.bash 脚本的提示:D。


继续阅读上述 3 行的更多细节:

Bash 具有出色的内置自动补全支持,但 bash 自动补全脚本不能直接在 zsh 中工作,因为 zsh 环境没有必要的 bash 自动补全帮助函数,例如 compgencomplete,这是为了保持 zsh 会话快速。

现在,zsh 随附了适当的完成脚本,如 compinitbashcompinit,它们具有支持 bash 自动补全脚本所需的函数。

autoload <func_name>:请注意,autoload 是在 zsh 中定义的,而不是 bash。 autoload 在由 fpath 命令返回的目录路径中查找名为 <func_name> 的文件,并在第一次调用该函数时标记为加载相同的文件。

  • -U:加载函数(如 compinit 或 bashcompinit)时忽略任何别名
  • +X:现在只是加载命名的函数,不要执行它

例如,在我的系统上,echo $fpath 返回 /usr/share/zsh/site-functions/usr/share/zsh/5.0.5/functions,并且两者都可在 /usr/share/zsh/5.0.5/functions 找到 compinitbashcompinit

此外,对于大多数人来说,可能只需要运行 autoload -U +X bashcompinit && bashcompinit,因为某些其他脚本(如 git 自动补全或他们自己的 ~/.zshrc)可能正在执行 autoload -U +X compinit && compinit,但同时运行这两个命令是安全的。


1
更新的答案,含更多细节。 - Jatin Kumar
6
这仍然没有多少意义; manpage 描述了 +X foo 的作用是做与调用 foo 相同的自动加载,但不立即执行它...所以 autoload +X foo && foo 看起来完全是多余的。你能解释一下为什么选择这种模式吗? - ELLIOTTCABLE
2
@ELLIOTTCABLE 这样做 autoload -U +X bashcompinit && bashcompinit 的目的是(由于 &&)只有在实际找到模块时才会执行 bashcompinit。普通的 autoload foo 总是返回 true。稍后,当您尝试运行 foo 时,zsh 最终会告诉您“找不到函数定义文件”。使用 autoload +X,您可以立即获得错误信息。 - FeRD
3
手册在这一点上仍然有些不清楚,对我来说似乎是这样的:autoload +X加载函数但不执行它-这比没有+Xautoload更多。没有+Xautoload甚至不会加载函数;它只是标记名称,表示稍后将要加载一个函数。实际的加载直到首次调用该函数才会尝试。如果您想确定命名的函数是否成功加载,您需要使用+X立即执行加载步骤。 - FeRD
1
@BrunoBronosky Zsh 可以让人感到挑战性。它的文档模糊不清,尽管有这么多的文档,但仍然感觉像很多功能要么是未记录的,要么是其文档非常难以找到。即使存在的文档也是一个混合包。有时我很难确定我正在阅读的是对混乱功能的有力记录尝试,还是一种被混乱地记录的功能。(#为什么不两者都有?) - FeRD
显示剩余3条评论

44

来自这个页面(日期为2010/01/05):

Zsh可以处理bash自动完成函数。 Zsh的最新开发版本具有一个名为bashcompinit的函数,当运行该函数时,它将允许zsh读取bash完成规范和函数。这在zshcompsys手册中有记录。要使用它,您只需在compinit之后的任何时间运行bashcompinit即可。它会定义与bash内置complete和compgen函数对应的complete和compgen函数。


好消息是: 我认为这应该可以工作,所以非常感谢你。坏消息是:由于某种原因,在我通过SSH登录时,我尝试完成此操作的系统似乎不加载/etc/zshrc。我对登录进程了解不够,无法确定原因。也许现在是提出另一个问题的时候了... - Coderer
1
@Coderer:首先,你说的是“/etc/zshrc”。在我的系统上,它是“/etc/zsh/zshrc”。此外,请按照相反的顺序检查以下文件(按照zsh启动时它们被源的顺序列出):“/etc/zsh/zshenv”、“$ZDOTDIR/.zshenv”、“/etc/zsh/zprofile”和“$ZDOTDIR/.zprofile”(可能是“~/.zprofile”),以查看是否未设置“RCS”变量。如果是,则会防止在其后的文件中源文件。最后,请检查“/etc/passwd”中用户的shell,并确保它是“zsh”,并确保它没有“-f”参数。 - Dennis Williamson
感谢所有的反馈 - 我采用了#1经典程序员技巧,“stdout调试”(在每个启动脚本中添加echo语句),并找到了问题所在。由于某种原因,我们的/etc/zprofile正在源代码各种/etc/profile.d脚本,然后在/etc/zshrc中再次被源代码。将autoload语句简单地移动到/etc/zprofile的顶部即可解决问题。 - Coderer

5

我正在使用Antigen作为Oh-My-Zsh插件管理器。 我有几个同事编写的bash完成脚本,我想用简单的source /path/to/completion将它们加载到Zsh中。

我遇到了一些困难,因为似乎Antigen或OMZ(很难确定)只关心从它们的插件加载完成脚本。 最终,我通过在antigen apply之后autoload bashcompinitcompinit来解决了这个问题。 仅仅autoload bashcompinit是不够的。

source ~/.antigen/antigen.zsh
antigen use oh-my-zsh
antigen apply

autoload -U +X compinit && compinit
autoload -U +X bashcompinit && bashcompinit

source /path/to/bash_completion

Antigen会在$ANTIGEN_COMPDUMP处创建它的.zcompdump文件,对我来说是~/.antigen/.zcompdump
重新调用compinit和bashcompinit会在$HOME/.zcompdump处创建第二个.zcompdump文件。
这似乎都能正常工作,因为我能够使用由/path/to/bash_completion设置的补全。我已经删除了几次.zcompdump文件以确保它们被重新生成,并且似乎可以正常工作。
有时在机器重启后,我不得不删除.zcompdump文件,因为尝试进行tab补全时会出现错误,但我不确定是由于此设置还是其他原因。执行rm ~/.zcompdump && rm $ANTIGEN_COMPDUMP并打开一个新的shell就可以解决问题。
撰写时使用的版本:
Antigen = v2.2.3 = d3d4ee0
Oh-my-zsh = c3b072e
Zsh = 5.3

5

对于 zsh 使用:

  • compdef
  • compadd

我的示例:

# bash completion for bxrun (/home/ecuomo/projects/bashx/bxrun)
_bxrun_methods() {
    grep "^\s*\(function\s\+\)\?__.\+()\s*{.*$" "${1}" | while read line ; do
        echo "$line" | sed "s/()\s*{.*//g" | sed "s/\s*\(function\s\+\)\?__//g"
    done
}
_bxrun_lst() {
    if [ -d "/home/ecuomo/projects/bashx/src/actions" ]; then
        for f in /home/ecuomo/projects/bashx/src/actions/* ; do
            if [ -f "${f}" ]; then
                basename "${f}" | sed 's/\..*$//g'
            fi
        done
    fi
    _bxrun_methods "/home/ecuomo/projects/bashx/bxrun"
    _bxrun_methods "/home/ecuomo/projects/bashx/src/bashx.sh"
}
_bxrun() {
    local cur
    COMPREPLY=()
    cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $( compgen -W '$( _bxrun_lst )' -- $cur  ) )
}
_bxrun_zsh() {
    compadd `_bxrun_lst`
}
if type complete >/dev/null 2>/dev/null; then
    # bash
    complete -F _bxrun bxrun
else if type compdef >/dev/null 2>/dev/null; then
    # zsh
    compdef _bxrun_zsh bxrun
fi; fi

来源:我的代码 https://github.com/reduardo7/bashx


4

@JatinKumar的回答让我找到了正确的方向,但我必须使用complete而不是source。所以全部内容如下:

autoload -Uz compinit && compinit
autoload -U +X bashcompinit && bashcompinit

complete -C /usr/local/bin/terraform terraform
complete -C /usr/local/aws/bin/aws_completer aws
complete -C /usr/local/bin/az az

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接