Пользовательские слова, завершающие табуляцию, которые могут включать пробелы, раздражающе сложны. И, насколько я знаю, элегантного решения нет. Возможно, какая-то будущая версия compgen
будет достаточно любезна, чтобы создавать массив, а не выводить возможности по одной строке за раз, и даже принимать аргумент списка слов из массива. Но до тех пор может помочь следующий подход.
Важно понимать проблему, заключающуюся в том, что ( $(compgen ... ) )
представляет собой массив, полученный путем разделения вывода команды compgen
на символы в $IFS
, которые по умолчанию представляют собой любые пробельные символы. Итак, если compgen
возвращает:
roger waters
richard wright
тогда COMPREPLY
будет фактически установлен в массив (roger waters richard wright)
, всего четыре возможных завершения. Если вместо этого вы используете ( "$(compgen ...)")
, то COMPREPLY
будет установлен в массив ($'roger waters\nrichard wright')
, который имеет только одно возможное завершение (с новой строкой внутри завершения). Ни то, ни другое не то, что вы хотите.
Если ни одно из возможных завершений не имеет символа новой строки, вы можете договориться о том, чтобы возврат compgen
был разделен на символ новой строки, временно сбросив IFS
, а затем восстановив его. Но я думаю, что более элегантным решением будет просто использовать mapfile
:
_test () {
cur=${COMP_WORDS[COMP_CWORD]};
use=`pink`;
## See note at end of answer w.r.t. "$cur" ##
mapfile -t COMPREPLY < <( compgen -W "$use" -- "$cur" )
}
Команда mapfile
помещает строки, отправленные compgen
в stdout
, в массив COMPREPLY
. (Опция -t
приводит к удалению завершающей новой строки из каждой строки, что почти всегда требуется при использовании mapfile
. Дополнительные параметры см. в help mapfile
.)
Это не решает другую раздражающую часть проблемы, заключающуюся в преобразовании списка слов в форму, приемлемую для compgen
. Поскольку compgen
не допускает множественных опций -W
и не принимает массив, единственным вариантом является форматирование строки таким образом, чтобы bash
разбиение на слова (с кавычками и всем остальным) генерировало желаемый список. По сути, это означает добавление escape-последовательности вручную, как вы сделали в своей функции pink
:
pink() {
echo "nick\ mason syd-barrett david_gilmour roger\ waters richard\ wright"
}
Но это подвержено несчастным случаям и раздражает. Лучшее решение позволило бы напрямую указывать альтернативы, особенно если альтернативы генерируются каким-то образом. Хороший способ создания альтернатив, которые могут включать пробелы, — поместить их в массив. Имея массив, вы можете эффективно использовать формат %q
printf
для создания входной строки с правильными кавычками для compgen -W
:
# This is a proxy for a database query or some such which produces the alternatives
cat >/tmp/pink <<EOP
nick mason
syd-barrett
david_gilmour
roger waters
richard wright
EOP
# Generate an array with the alternatives
mapfile -t pink </tmp/pink
# Use printf to turn the array into a quoted string:
_test () {
mapfile -t COMPREPLY < <( compgen -W "$(printf '%q ' "${pink[@]}")" -- "$2" )
}
Как написано, эта функция завершения не выводит завершения в форме, которая будет принята bash как отдельные слова. Другими словами, завершение roger waters
генерируется как roger waters
вместо roger\ waters
. В (вероятном) случае, когда цель состоит в том, чтобы создать правильно цитируемые завершения, необходимо добавить escape-последовательности во второй раз после того, как compgen
отфильтрует список завершения:
_test () {
declare -a completions
mapfile -t completions < <( compgen -W "$(printf '%q ' "${pink[@]}")" -- "$2" )
local comp
COMPREPLY=()
for comp in "${completions[@]}"; do
COMPREPLY+=("$(printf "%q" "$comp")")
done
}
Примечание. Я заменил вычисление $cur
на $2
, так как функция, вызываемая через complete -F
, получает команду как $1
, а слово завершается как $2
. (Предыдущее слово также передается как $3
.) Кроме того, важно заключать его в кавычки, чтобы оно не разделялось на слова на пути к compgen
.
person
rici
schedule
22.10.2014
array=("part one" "part two")
- person amphetamachine   schedule 22.10.2014array=( $( echo \"part one\" \"part two\" ) )
приводит к массиву из 4 элементов,echo ${array[0]}
напечатает"part
. - person geronimo   schedule 22.10.2014