我如何循环可能的完成?

我如何循环可能的完成?

我希望能够获得给定部分命令的一系列可能的完成结果。例如,部分命令service um有以下可能的完成方式:

$ service um<TAB><TAB>
umountfs      umountnfs.sh  umountroot

我想要一个具有completions以下行为的函数:

$ for x in $(completions 'service um'); do
> echo $x
> done
umountfs
umountnfs.sh
umountroot

部分进展:到目前为止我所学到的

我认为这是一种可以成为完整答案的方法。我绝对希望看到完整的答案,但考虑到相对简单的方法<TAB><TAB>以非编程方式提供了相同的功能,感觉还可能有一个更灵活的解决方案。

我可以发现该service命令的完成机制是_service函数:

$ complete -p service
complete -F _service service

当调用这个完成函数时_service,会设置一堆环境变量(即COMP_{LINE,POINT,KEY,TYPE,WORDS,CWORD};参见bash 手册页),该函数以正在完成的命令、正在完成的单词和前一个单词作为参数给出,并填充COMPREPLY可能的完成。所以我想要的completions函数可以定义如下:

function completions() {
    # Produce an array of tokens in the input.
    read -a words <<< $1

    # Use "complete -p ${words[0]}" to determine how the 
    # completions are computed. This could be complicated
    # if complete is given flags other than -F.
    completion_func=???

    # Set all those COMP_* environment variables appropriately.

    # Run the function to populate COMPREPLY. This version
    # assumes words has length at least 2, but that can be
    # fixed.
    $completion_func ${words[0]} ${words[-1]} ${words[-2]}

    echo ${COMPREPLY[@]}
}

除了相对复杂性之外<TAB><TAB>,这种方法的一个缺点是它改变了环境。

答案1

这是一个我认为可以作为起点的基本函数。它可能会以多种方式失败,希望这里的其他人可以改进:

completions () (
    if [ -f /usr/share/bash-completion/bash_completion ]; then
        . /usr/share/bash-completion/bash_completion
    elif [ -f /etc/bash_completion ]; then
        . /etc/bash_completion
    fi

    IFS="$COMP_WORDBREAKS" read -a words <<<"$1"
    complete_setting=($(complete -p "${words[0]}"))
    complete_optstring=":abcdefgjksuvprDEo:A:G:W:F:C:X:P:S:"
    while getopts "$complete_optstring" option "${complete_setting[@]:1}"
    do
        case $option in
            F) complete_functon="$OPTARG"
                ;;
            *) # Run around screaming!                    
                ;;
        esac
    done
    COMP_WORDS=("${words[@]}")
    COMP_LINE="$1"
    COMP_POINT="${#COMP_LINE}"
    COMP_CWORD=$((${#COMP_WORDS[@]} - 1))
    "$complete_functon" 
    printf "%s\n" "${COMPREPLY[@]}"
)

笔记:

  • 如果您将其用作交互式 shell 的函数,则不需要初始源。
  • complete使用 查找单词 split COMP_WORDBREAKS,因此我们将IFS其设置为 for read
  • complete -p以可重用的方式打印出当前的完成设置,因此我们可以按照原来的方式解析选项。
  • 该函数使用子 shell(()而不是{}),因此您当前的环境不应受到干扰。

相关内容