嵌套 bash 自动完成脚本

发布于 2025-01-06 10:36:01 字数 1884 浏览 0 评论 0原文

我一直在尝试向我最近使用的命令行程序添加 bash 完成支持,但似乎遇到了困难。

以下是我想要自动完成的命令和子命令

  • 主命令是foo,应该对子命令versionprocess<执行自动完成/code> 和帮助。进一步的自动完成取决于子命令。

    • 如果用户在主命令后输入 -,自动补全功能会完成这些单词:--activate--deactivate >

    • 如果用户输入 --deactivate,它应该执行 bash 命令输出的自动完成(假设为 ls)。

  • version:不执行自动补全
  • process:自动补全默认列出目录(包括当前目录和父目录)

    • 如果用户输入 -,自动补全功能会执行这些单词的补全:--color--verbose,然后停止李>
  • 如果用户输入 help,自动补全适用于其他子命令(process或者version)。

这是我当前的自动完成脚本实现(严重失败):

_foo() {
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    words=("${COMP_WORDS[@]}")
    cword=$COMP_WORD
    opts="process version help"

    case "$prev" in
    -*)
        local subopts="--activate --deactivate"
            COMPREPLY=( $(compgen -W "${subopts}" -- ${cur}) )
            case "$cur" in
                --deactivate)
                    COMPREPLY=( $(ls) )
                ;;
            esac
         ;;
    version)
        COMPREPLY=()
        ;;
    process)
        case "$cur" in
            -*)
                local subopts="--color --verbose"
                COMPREPLY=( $(compgen -W "${subopts}" -- ${cur}) )
                ;;
            *)
                COMPREPLY=( $(compgen -A directory))
                ;;
        esac
        ;;
    help)
        COMPREPLY=( $(compgen -W "process version" -- ${cur}) )
        ;;
   esac
   } 
 complete -F _foo foo

您可能会发现 Bash 也不是我的主要强项。我正在考虑为每个子命令编写单独的 bash 函数,但我现在不知道该怎么做。如果您也对如何实现这一点有建议,我将非常感激。

谢谢!

I've been trying to add bash completion support to a command line program I've been using lately, but it seems that I've hit a wall.

Here are the commands and subcommands I'd like to auto-complete

  • Main command is foo, autocompletion should be performed for the subcommands version, process, and help. Further autcompletion depends on the subcommand.

    • If the user enters - after the main command, autocompletion performs completion for these word: --activate or --deactivate

    • If the user enters --deactivate, it should perform autocompletion of a bash command output (let's say ls).

  • version: no autocompletion performed
  • process: autocompletion defaults to listing directories (current and parent included)

    • If the user enters -, autocompletion performs completion for these words: --color, --verbose and then stops afterward
  • If the user enters help, autocompletion is for the other subcommands (process or version).

Here's my current implementation of the autocomplete script (which is failing badly):

_foo() {
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    words=("${COMP_WORDS[@]}")
    cword=$COMP_WORD
    opts="process version help"

    case "$prev" in
    -*)
        local subopts="--activate --deactivate"
            COMPREPLY=( $(compgen -W "${subopts}" -- ${cur}) )
            case "$cur" in
                --deactivate)
                    COMPREPLY=( $(ls) )
                ;;
            esac
         ;;
    version)
        COMPREPLY=()
        ;;
    process)
        case "$cur" in
            -*)
                local subopts="--color --verbose"
                COMPREPLY=( $(compgen -W "${subopts}" -- ${cur}) )
                ;;
            *)
                COMPREPLY=( $(compgen -A directory))
                ;;
        esac
        ;;
    help)
        COMPREPLY=( $(compgen -W "process version" -- ${cur}) )
        ;;
   esac
   } 
 complete -F _foo foo

You can probably see that Bash is not my main forte as well. I was thinking of writing separate bash functions for each subcommands, but I don't know how to do it at the moment. If you have suggestions on how to implement this as well, I would really appreciate it.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

绅士风度i 2025-01-13 10:36:01

对于第一级可能的子命令,我有点不清楚。
是否可以输入 foo --activatefoo - --activate ?后者看起来有些奇怪,但更适合您给定的代码。
第一个听起来更合理,但意味着将 --activate 等作为全局参数,与子命令一样在同一级别上进行处理。

然而,当您尚未输入任何内容时,您错过了默认值。
尝试以下代码:

_foo() {
    local cur prev opts

    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    words=("${COMP_WORDS[@]}")
    cword=$COMP_CWORD
    opts="process version help"

    case "$prev" in
    -*)
        local subopts="--activate --deactivate"
        COMPREPLY=( $(compgen -W "${subopts}" -- ${cur}) )
        case "$cur" in
            --deactivate)
                COMPREPLY=( $(ls) )
            ;;
        esac
        return 0
         ;;
    version)
        COMPREPLY=()
        return 0
        ;;
    process)
        case "$cur" in
            -*)
                local subopts="--color --verbose"
                COMPREPLY=( $(compgen -W "${subopts}" -- ${cur}) )
                ;;
            *)
                COMPREPLY=( $(compgen -A directory))
                ;;
        esac
        return 0
        ;;
    help)
        COMPREPLY=( $(compgen -W "process version" -- ${cur}) )
        return 0
        ;;
   esac
   COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
   return 0
} 
complete -F _foo foo

它与您的代码基本相同,但是:
在底部,将 COMPREPLY 设置为您的第一级子命令,以便它们完成。
如果输入了子命令,则应返回 (0),以免到达最终语句。

给出的示例将与 foo - --activate 一起使用,这可能不是您想要的。
因此,如果您想输入 foo --activate 将相应的行更改为

    case "$prev" in
    --activate)
        COMPREPLY=()
        return 0
        ;;
    --deactivate)
        COMPREPLY=( $(compgen -W "$(ls)" -- ${cur}) )
        return 0
        ;;
    version)
    ...

In case of the possible subcommands on first level it's a bit unclear for me.
Should it be possible to enter foo --activate or foo - --activate? The latter looks somehow strange but fits more likely to your given code.
The first sounds more reasonable, but would imply to have --activate etc. as kind of global parameter that is to be treated on the same level like your subcommands.

Nevertheless you missed the default, when you haven't entered anything yet.
Try the following code:

_foo() {
    local cur prev opts

    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    words=("${COMP_WORDS[@]}")
    cword=$COMP_CWORD
    opts="process version help"

    case "$prev" in
    -*)
        local subopts="--activate --deactivate"
        COMPREPLY=( $(compgen -W "${subopts}" -- ${cur}) )
        case "$cur" in
            --deactivate)
                COMPREPLY=( $(ls) )
            ;;
        esac
        return 0
         ;;
    version)
        COMPREPLY=()
        return 0
        ;;
    process)
        case "$cur" in
            -*)
                local subopts="--color --verbose"
                COMPREPLY=( $(compgen -W "${subopts}" -- ${cur}) )
                ;;
            *)
                COMPREPLY=( $(compgen -A directory))
                ;;
        esac
        return 0
        ;;
    help)
        COMPREPLY=( $(compgen -W "process version" -- ${cur}) )
        return 0
        ;;
   esac
   COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
   return 0
} 
complete -F _foo foo

which is basically the same as yours but:
At the bottom, set COMPREPLY to your first level subcommands so they get completed.
In case of having entered a subcommand, you should return (0) to not reach the final statement.

The example given will work with foo - --activate which may not what you wanted.
So if you wanted to enter foo --activate change the according lines to

    case "$prev" in
    --activate)
        COMPREPLY=()
        return 0
        ;;
    --deactivate)
        COMPREPLY=( $(compgen -W "$(ls)" -- ${cur}) )
        return 0
        ;;
    version)
    ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文