Окончание баша для значений, разделенных запятыми

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

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-completions есть некоторые связанные функции для csv-lists?

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

Лучшее, что я мог придумать, состоит в том, COMPREPLY создать COMPREPLY только с первым словом, имеющим весь префикс ( COMPREPLY=( "${prefix},"$(compgen -W "${servers[@]}" -- ${realcur}) ) , так что, если есть только одно возможное завершение, оно выполняется автоматически автоматически, а если есть более одного возможного завершения, то bash не удалит то, что было напечатано до сих пор (потому что первое слово в COMPREPLY имеет весь префикс и, таким образом, соответствует 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 до $COMP_CWORD пор, пока вы не найдете вариант (строка, которая соответствует -* ), и если это «-s», вам нужно выполнить завершение имени сервера.