Как предотвратить завершение bash после замены символа при заполнении вкладки - программирование
Подтвердить что ты не робот

Как предотвратить завершение bash после замены символа при заполнении вкладки

Я создаю bash завершение script для инструмента, который разделяет загрузку семантики с помощью curl.

С помощью curl вы можете сделать:

curl -F var = @file

чтобы загрузить файл.

Мое приложение имеет схожую семантику, и я хочу иметь возможность показывать возможные файлы после нажатия кнопки "@" . К сожалению, это сложно:

cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
if [[ "$cur" == @* && "$prev" == '=' ]]; then
    COMPREPLY=( $(compgen -f ${cur:1}) )
    return 0
fi 

Итак, если команда (до сих пор) заканчивается:

[email protected]

Отобразятся файлы в текущем каталоге.

 [email protected]/usr/                                                                                                                       
 /usr/bin      /usr/games 

Проблема в том, что если я на самом деле нажимаю вкладку, чтобы завершить, "@" уходит!

 var=/usr/bin

Итак, похоже, что bash заменяет все текущее слово на вкладку COMPREPLY.

Единственный способ избежать этого - сделать это:

        COMPREPLY=( $(compgen -f ${cur:1}) )
        for (( i=0; i<${#COMPREPLY[@]}; i++ )); 
        do 
            COMPREPLY[$i]='@'${COMPREPLY[$i]} 
        done                                                                                                                       

Но теперь завершение вкладки выглядит странно, если не сказать больше:

@/usr/bin      @/usr/games

В любом случае, чтобы показать обычное заполнение вкладки файла (без префикса '@'), но сохраните "@" при нажатии на вкладку?

4b9b3361

Ответ 1

Итак, это заинтриговало меня, поэтому я читал через источник завершения bash (доступен в /etc/bash _completion).

Я столкнулся с этой переменной: ${COMP_WORDBREAKS}, которая позволяет контролировать, какие символы используются для разграничения слов.

Я также столкнулся с этой функцией _get_cword и дополнительным _get_pword, рекомендованным для использования вместо ${COMP_WORDS[COMP_CWORD]} и ${COMP_WORDS[COMP_CWORD-1]} соответственно.

Итак, собрав все это, я провел некоторое тестирование и вот что я придумал: это, похоже, работает для меня, по крайней мере, надеюсь, это будет и для вас:

# maintain the old value, so that we only affect ourselves with this
OLD_COMP_WORDBREAKS=${COMP_WORDBREAKS}
COMP_WORDBREAKS="${COMP_WORDBREAKS}@"

cur="$(_get_cword)"
prev="$(_get_pword)"

if [[ "$cur" == '[email protected]' ]]; then
    COMPREPLY=( $(compgen -f ${cur:2}) )

    # restore the old value
    COMP_WORDBREAKS=${OLD_COMP_WORDBREAKS}
    return 0
fi

if [[ "$prev" == '[email protected]' ]]; then
    COMPREPLY=( $(compgen -f ${cur}) )

    # restore the old value
    COMP_WORDBREAKS=${OLD_COMP_WORDBREAKS}
    return 0

fi

Теперь, я признаю, что случаи раскола if немного грязные, определенно лучший способ сделать это, но мне нужно больше кофеина.

Кроме того, что бы он ни стоил вам, я также обнаружил по пути аргумент -P <prefix> для compgen, что помешало бы вам зациклиться на массиве $COMPREPLY[*]} после вызова compgen, например

COMPREPLY=( $(compgen -P @ -f ${cur:1}) )

Это немного избыточно перед лицом полного решения.

Ответ 2

Попробуйте это....

function test
{
  COMPREPLY=()
  local cur="${COMP_WORDS[COMP_CWORD]}"
  local opts="Whatever sort of tabbing options you want to have!"
  COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
}
complete -F "test" -o "default" "test"
# complete -F "test" -o "nospace" "sd" ## Use this if you do not want a space after the completed word