Завершение Bash для значений, разделенных запятыми

16

Я хотел бы создать правило завершения для списка параметров, разделенных запятыми. Например, у меня есть команда, которая получает список имен серверов:

myscript -s name1,name2,name3

На данный момент мне удалось написать следующее завершение:

_myscript () {
  local cur prev opts

  _get_comp_words_by_ref cur prev

  opts='-s'

  servers='name1 name2 name3'

  if [[ ${cur} == -* ]] ; then
    COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
  else
    case "${prev}" in
      -s)
        if [[ "$cur" == *,* ]]; then
          local realcur prefix
          realcur=${cur##*,}
          prefix=${cur%,*}
          COMPREPLY=( $(compgen -W "${servers}" -P "${prefix}," -- ${realcur}) )
        else
          COMPREPLY=( $(compgen -W "${servers}" -- ${cur}) )
        fi
        ;;
      *)
        # do nothing
        ;;
    esac
  fi
}

Но у него есть как минимум 2 проблемы:

  1. Предложения по текущему значению включают все предыдущие значения в их префиксе.
  2. Он не учитывает повторяющиеся значения.

Каковы лучшие практики для таких случаев? Может быть, в bash-дополнениях есть несколько связанных функций для csv-списков?

diffycat
источник
3
Что может помочь, так это то, что вы можете разделить значения, разделенные запятыми, в итерируемый список, например так: IFS=, LIST=("$VARIABLE")где $ VARIABLE содержит значения, разделенные запятыми.
Майкл Эренрайх,
2
Хорошая идея @MichaelEhrenreich, но вы не должны цитировать $VARIABLE, иначе разрыва слов не произойдет. просто используйте IFS=, LIST=($VARIABLE).
Guss

Ответы:

6

По сути, нет способа исправить проблемы, которые вы описываете, потому что bash использует значения COMPREPLYнепосредственно на дисплее, а затем заменяет текст пользователя - в то время как для получения того, что вы хотите, вам нужно сначала сгенерировать возможные дополнения (только дополнительные имена серверов, без префикса) для отображения bash, затем, когда bash собирается заменить пользовательский текст самой длинной неконфликтующей строкой, вам потребуется, чтобы он снова вызвал ваш скрипт для генерации текста с префиксом - и bash не имеет возможности для этого.

Лучшее, что я мог бы придумать, - это COMPREPLYгенерировать только первое слово, имеющее весь префикс ( COMPREPLY=( "${prefix},"$(compgen -W "${servers[@]}" -- ${realcur}) )), так что, если есть только одно возможное завершение, оно завершается автоматически правильно, в то время как при наличии более одного возможного завершения , тогда bash не удалит то, что было набрано до сих пор (поскольку первое слово в COMPREPLYимеет полный префикс и, следовательно, соответствует набранному в настоящее время тексту, и будет выбрано bash для замены текста пользователя), и отобразит параметры без префикса - кроме для этого одного слова, которое уже содержит префикс, поэтому вывод будет выглядеть так:

$ command -s banana,a
ananas     apricot    banana,apple

«Яблоко», как отсортировано последним в опциях завершения, потому что оно содержит префикс, который начинается с «b» - очень запутанно. Поэтому я не рекомендую это делать.

Что касается дубликатов - чтобы не отображать дубликаты, вам просто нужно разбить $prefixего на части (easy IFS="," prefix_parts=($prefix):), а затем перебрать их и оставить только в $serversименах, которые еще не перечислены. Его набирать утомительно, поэтому я не буду показывать это здесь, но относительно тривиально, так что я уверен, что вы можете справиться :-).

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

Вы можете поддерживать формат параметров, подобный следующему: command -s <server> [<server> [..]]и затем для завершения записей, отличных от той, которая находится сразу после -sпараметра, просто сканируйте $COMP_WORDSмассив обратно, $COMP_CWORDпока не найдете параметр (совпадающая строка -*), и если его «-s» затем вам нужно сделать завершение имени сервера.

Guss
источник