用于自定义命令的动态zsh自动完成


19

我正在尝试为我编写的一些自定义函数编写完成函数,但是即使是最基本的函数,似乎也很挣扎。

一个示例函数是:

function eb_instances() {
    if [ "$#" -ne 2 ]; then
        echo "Usage eb_instances <aws profile name> <environment name>"
        echo "e.g.:"
        echo " eb_instances production kraken-prod-api"
        return 1
    fi

    aws ec2 describe-instances --filters  "Name=instance-state-name,Values=running"   "Name=tag:Name,Values=$2" --profile=$1 --output=json | jq -r ".Reservations[].Instances[].PrivateIpAddress"
}

这有两个位置参数,<aws profile name>以及<environment name>

我希望<aws profile name>可以通过运行来动态获得 完成选项sed -n -E 's/\[([a-zA-Z0-9_\-]+)\]/\1/p' ~/.aws/credentials | tr \\n ' ',并<environment name>希望通过运行我调用的另一个函数来动态实现完成选项eb_names

我发现文档非常稀疏且难以遵循。我也看到过类似命令的zsh-completions存储库,但似乎找不到与我需要的东西类似的东西。

任何帮助入门将不胜感激!

更新资料

基于@cuonglm的答案,我使用了:

#compdef ebinstances

_ebinstances() {
  local state

  _arguments \
    '1: :->aws_profile'\
    '*: :->eb_name'

  case $state in
    (aws_profile) _arguments '1:profiles:($(sed -n -E "s/\[([a-zA-Z0-9_\-]+)\]/\1/p" ~/.aws/credentials | tr \\n " "))' ;;
              (*) compadd "$@" foo bar
  esac
}

_ebinstances "$@"

我在原始问题中忘记提及的是,我还希望第二个参数的完成取决于第一个参数(这两个参数都是基于动态执行某些代码),例如:

$ eb_instances <cursor>TAB
cuonglm  test

得到我想要的完成。一旦选择第一个,然后尝试自动完成:

$ eb_instances cuonglm <cursor>TAB

我想通过执行来生成补全选项eb_names cuonglm,并且,如果可能的话,还可以对补全进行深入分析,例如,如果正确的候选人是foo-bar

$ eb_instances cuonglm foo<cursor>TAB

我想通过执行生成完成选项 eb_names cuonglm foo

Answers:


23

第一次,zsh完成系统似乎非常复杂且难以掌握。让我们尝试一个例子。

您需要了解的第一件事,zsh完成系统将从中加载完成功能$fpath。确保您的完成目录出现在:

print -rl -- $fpath

(如果您使用过 oh-my-zsh,则.oh-my-zsh/completions存在$fpath,您可以创建它并将完成功能放在此处)

现在,您必须为函数创建一个完成文件,该文件的名称必须以underscore开头_,再加上您的函数名称。在您的情况下,其名称为_eb_instances

将这些行添加到 _eb_instances文件:

#compdef eb_instances

_eb_instances() {
  local state

  _arguments \
    '1: :->aws_profile'\
    '*: :->eb_name'

  case $state in
    (aws_profile) _arguments '1:profiles:(cuonglm test)' ;;
              (*) compadd "$@" prod staging dev
  esac
}

_eb_instances "$@"

大功告成 保存文件并开始新的会话以测试完成。您将看到类似以下内容:

$ eb_instances <cursor>TAB
cuonglm  test

$ eb_instances cuonglm <cursor>TAB
dev      prod     staging

您可以阅读有关_arguments功能,state变量的zsh完善系统文档。你也需要改变(cuonglm test)你的sed命令,并改变prod staging dev你的eb_names功能。

如果要基于传递的第一个参数生成第二个参数,可以使用$words[2]变量:

case $state in
  (aws_profile) _arguments '1:profiles:(cuonglm test)' ;;
            (*) compadd "$@" $(echo $words[2]) ;;
esac

更换 echo用您的实际函数,在您的情况下为$(eb_names $words[2])

如果您仍然感到难以实现,只需定义_eb_instances,然后eb_instances在您的.zshrc调用中将完成定义为:

compdef _eb_instances eb_instances

您需要使用以下内容初始化完成系统:

autoload -U compinit
compinit

(如果使用oh-my-zsh,它已被加载)


非常感谢您的详细回复。尽管我似乎找不到一个有据可查的方法来解决问题,但是,如果我的完成选项是动态的,还是来自其他函数/脚本,那该怎么办?即,在您的示例中,如果我想cuonglm test来自sed -n -E 's/\[([a-zA-Z0-9_\-]+)\]/\1/p' ~/.aws/credentials | tr \\n ' '
zsquare

@zsquare:替换(cuonglm test)($(sed -n -E 's/\[([a-zA-Z0-9_\-]+)\]/\1/p' ~/.aws/credentials | tr \\n ' ')),更改compadd "$@" prod staging devcompadd "$@" $(eb_names)
cuonglm

这可能会稍微延长一点,但是,我忘了提及,这需要传递eb_names第一个参数,即,productionstaging,我该怎么做?非常感谢,我已经整理了很多东西。
zsquare 2015年

我的意思是我不想使用第一个参数传递给eb_names。我尝试引用$1$2,但没有骰子。
zsquare 2015年

2
你是个巫师,哈利。
宏伟的
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.