自定义脚本的 zsh 补全:完成“case”语句中的选项

Mar*_*ter 3 command-line zsh autocomplete

我有自定义脚本,需要:

  1. 短/长格式的可选参数
  2. 一个必需的命令行参数

短/长命令行选项例如:

-r, --readonly
-m, --mount
Run Code Online (Sandbox Code Playgroud)

对于一个必需的参数,这些参数实际上在脚本中指定为 case 语句,即foobar本例中:

case $1 in
  foo )
  :
  ;;

  bar )
  :
  ;;
Run Code Online (Sandbox Code Playgroud)

如何zsh completion为我的脚本创建,以便在参数以 开头时完成可选参数-,并从我的脚本 case 语句中获取所需参数?

更新:

这是我到目前为止所得到的,受到答案的启发@Marlon Richert

假设调用我的自定义脚本myscript.sh并且我创建的完成规则位于/usr/share/zsh/functions/Completion/Unix/_myscript.sh

#compdef myscript.sh

_myscript () {
  local -a args

  args+=(
    {-r,--readonly}'[description for "readonly"]' 
    {-m,--mount}'[description for "mount"]' 
  )

  _arguments $args && return
}

_myscript "$@"
Run Code Online (Sandbox Code Playgroud)

我的脚本myscript.sh本身位于/usr/local/bin/myscript.sh.

因此,现在当我有了可选参数-r-m得到处理时,我需要修改我的完成规则,以便对于我的脚本所需的命令行参数,来自 case 语句的项目作为/usr/local/bin/myscript.sh完成提供。

另外,我不确定args+=(完成脚本中第 6 行开始的块的语法是否正确。我必须在哪里放置单引号?

Mar*_*ert 8

让我们假设您要为其定义完成的函数称为myfunc

\n

首先,让我们设置实际的功能:

\n
    \n
  1. 将您的函数放在名为myfunc. 没有10
      \n
    • 这不会以.zsh或结尾.sh
    • \n
    • 当您将函数放入其自己的文件中时,无需添加funcname() {\xe2\x80\xa6}样板文件。
    • \n
    \n
  2. \n
  3. 确保包含该文件的目录myfunc位于您的$fpath. 例如,如果文件myfunc位于 中~/Functions,请将其添加到您的~/.zshrc文件中:\n
    fpath+=( ~/Functions )\n
    Run Code Online (Sandbox Code Playgroud)\n
  4. \n
  5. 最后,自动加载myfunc到您的~/.zshrc文件中:\n
    # We pass a couple of options that make the code \n# less likely to break:\n# -U suppresses alias expansion\n# -z marks the function for zsh-style autoloading == \n#    `unsetopt KSH_AUTOLOAD`\nautoload -Uz myfunc\n
    Run Code Online (Sandbox Code Playgroud)\n
  6. \n
\n

您现在应该能够myfunc在命令行上使用(但还没有任何完成)。

\n

接下来,让我们创建完成函数:

\n
    \n
  1. 创建一个名为_myfunc.
  2. \n
  3. 放入此文件中:\n
    #compdef myfunc\n\n# The line above means "This function generates \n# completions for myfunc."\n# The combination of that line, plus the file name\n# starting with an `_`, plus having this file\'s \n# parent dir in your `$fpath`, ensures this file \n# will be autoloaded when you call `compinit`.\n\n# `+X` makes sure `myfunc`\'s definition will get \n# loaded immediately, even if you have not called \n# this function yet.\nautoload +X -Uz myfunc\n\n# Get the definition of `myfunc` in string form.\nlocal funcdef="$( type -f myfunc )"\n\n# Get the part that matches `case*esac`, then split\n# it on whitespace and put the resulting words in an \n# array.\nlocal -a words=( ${=funcdef[(r)case,(r)esac]} )\n\n# Keep only the words that start with `(` and end \n# with `)`.\n# Even if you used the `case` syntax with only the \n# closing `)`s, `type -f` will show your cases with\n# both `(` and `)`.\nlocal -a required=( ${(M)words:#\'(\'*\')\'} )\n\n# `-s`: Allow options to `myfunc ` to be stacked, \n# that is, you are allowed to specify `myfunc -rm`.\n# If not, remove the `-s` option.\n# `*:`: Let this argument be completed in any \n# position.\n_arguments -s \\\n    {-r,--readonly}\'[description for "readonly"]\' \\\n    {-m,--mount}\'[description for "mount"]\' \\\n    "*:required argument:( ${required//[()]/}  )"\n
    Run Code Online (Sandbox Code Playgroud)\n
      \n
    • 替换required argument为您想要调用的任何参数。
    • \n
    • 填写选项的说明。
    • \n
    \n
  4. \n
  5. 再次确保该文件所在的目录位于您的$fpath.
  6. \n
  7. 确保autoload -Uz compinit; compinit在您的.zshrc文件中执行此操作,并确保它上面的目录添加到您的$fpath.
  8. \n
  9. 重新启动 shell,exec zsh或关闭终端窗口并打开一个新窗口。
  10. \n
\n

您现在应该能够完成myfunc.

\n
\n

如果readonlymount互斥,您需要重写完成函数的最后一行,如下所示:

\n
_arguments \\\n    (-m --mount){-r,--readonly}\'[description for "readonly"]\' \\\n    (-r --readonly){-m,--mount}\'[description for "mount"]\' \\\n    "*:required argument:( ${required//[()]/}  )"\n
Run Code Online (Sandbox Code Playgroud)\n