Как bash завершить длинную опцию GNU с заданным набором аргументов? - PullRequest
2 голосов
/ 29 сентября 2019

GNU рекомендует использовать синтаксис - name = value для передачи аргумента для длинной опции. Это позволяет длинной опции принимать аргумент, который сам по себе необязателен.

Предположим, у вас есть полный набор возможных аргументов. Как вы пишете код завершения bash для такой опции? Я хочу, чтобы завершение добавляло пробел, когда оно завершает однозначный аргумент, но не раньше.

1 Ответ

1 голос
/ 18 октября 2019

Вот код шаблона, который я написал для завершения параметров GNU, приведенных в коде для воображаемой команды gnu-options.

Параметры, которые не принимают аргументы, определены в массиве opts. Опции, которые могут принимать аргумент, определены в ассоциативном массиве args.

. Скрипт поддерживает даже случай, когда $COMP_WORDBREAKS не включает '=', но показанные завершения длиннее, чем.

# Hack for given strings $2,$3,... possibly being in $1 and $COMP_WORDBREAKS
# Only the part after each match is listed as a completion.
# Run 'shopt -s extdebug; declare -F __ltrim_colon_completions; shopt -u extdebug'
# to see location for the respective function for colon only.
__ltrim_completions ()
{
    local cur=$1; shift
    while [[ ${1+x} ]]; do
        if [[ "$cur" == *$1* && "$COMP_WORDBREAKS" == *$1* ]]; then
            local x_word=${cur%$1*}$1
            local i=${#COMPREPLY[*]}
            while [[ $((--i)) -ge 0 ]]; do
                COMPREPLY[$i]=${COMPREPLY[$i]#"$x_word"}
            done
        fi
        shift
    done
}

_gnu_options()
{
    local IFS=$'\n' # needed for handling trailing space of some options and all arguments
    local cur prev words cword split # needed by _init_completion()
    local opts i prefix= wordlist
    local -A args=()
    # Do not treat = as word breaks even if they are in $COMP_WORDBREAKS:
    # Split option=value into option in $prev and value in $cur
    _init_completion -s || return

    # DEFINE OPTIONS THAT DO NOT TAKE AN ARGUMENT HERE:
    opts=(option0 option1)
    # DEFINE THE OPTIONS WITH ARGUMENTS HERE:
    args=([with-args0]= [with-args1]=$'arg10\narg11')
    args[with-args2]=\
'arg=20
arg=21
var=22
argx'
    args[with-args3]=

    for i in ${!args[*]}; do
        if [[ $prev = --$i ]]; then
            [[ "$COMP_WORDBREAKS" != *=* && $split == true ]] && prefix="--$i="
            if [[ ${args[$i]} ]]; then
                COMPREPLY=( $( compgen -P "$prefix" -W "${args[$i]}" -- "$cur" ) )
                __ltrim_completions "$cur" =
            else
                case $i in
                    with-args0)
                        # expand file/directory name.
                        COMPREPLY=( $( compgen -P "$prefix" -A file -- "$cur" ) )
                        compopt -o filenames
                        ;;
                    *)
                        COMPREPLY=()
                        ;;
                esac
            fi
            return 0
        fi
    done

    wordlist=()
    for i in ${opts[*]}; do wordlist+=("--$i "); done
    for i in ${!args[*]}; do wordlist+=("--$i="); done
    COMPREPLY=( $( compgen -W "${wordlist[*]}" -- "$cur" ) )
    compopt -o nospace
} && complete -F _gnu_options gnu-options
...