Ответ 1
bash
до версии 4.2 не позволяет контролировать выходной формат завершений, к сожалению.
Bash 4.2+ позволяет переключиться на вывод 1-предложение на строку в глобальном масштабе, как объясняется в полезный ответ Гриши Левит, который также ссылается на a умное обходное решение для достижения решения для каждой функции завершения.
Ниже приведено сложное обходное решение для настраиваемого завершения.
Решение этой проблемы в общем для всех определенных доработок было бы намного сложнее (если бы можно было напрямую вызвать функции readline
, это может быть проще, но я не знаю 't нашел способ сделать это).
Чтобы проверить доказательство концепции ниже:
- Сохраните файл и отправьте его (
. file
) в свою интерактивную оболочку - это будет:- определить команду с именем
foo
(функция оболочки) - аргументы которого основаны на совпадении имен файлов в текущем каталоге.
- (Когда
foo
действительно вызывается, он просто печатает свой аргумент в диагностической форме.)
- определить команду с именем
- Вызывать как:
foo [fileNamePrefix]
, затем нажмите tab:- Если между 2 и 9 файлами в текущем каталоге совпадают, вы увидите желаемый по очереди вывод.
- В противном случае (1 совпадение или 10 или более совпадений) произойдет нормальное завершение.
Ограничения
- Завершение работы работает только при применении к аргументу LAST в редактируемой командной строке.
- Когда заполнение фактически вставлено в командную строку (как только совпадение однозначно), к нему не добавляется пробел (это поведение требуется для обходного пути).
- Повторная переадресация запроса в первый раз после печати выходного файла с автоматическим форматированием может работать некорректно: повторная перерисовка командной строки, включая приглашение, должна быть смоделирована, и поскольку нет прямого способа получить расширенную версию строки определения подсказки, хранящейся в
$PS1
используется обходной путь (вдохновленный fooobar.com/questions/283220/...), который должен работать в типичных случаях, но не является надежным.
Подход
- Определяет и назначает пользовательскую функцию оболочки завершения для интересующей команды.
- Пользовательская функция определяет совпадения и, если их счетчик находится в нужном диапазоне, обходит нормальный механизм завершения и создает выходные данные в формате. format.
- Выход в формате форматированного текста (каждое совпадение в собственной строке) отправляется непосредственно на терминал
>/dev/tty
, а затем строка приглашения и командная строка вручную перерисовываются, чтобы имитировать стандартное поведение завершения. - См. комментарии в исходном коде для деталей реализации.
# Define the command (function) for which to establish custom command completion.
# The command simply prints out all its arguments in diagnostic form.
foo() { local a i=0; for a; do echo "\$$((i+=1))=[$a]"; done; }
# Define the completion function that will generate the set of completions
# when <tab> is pressed.
# CAVEAT:
# Only works properly if <tab> is pressed at the END of the command line,
# i.e., if completion is applied to the LAST argument.
_complete_foo() {
local currToken="${COMP_WORDS[COMP_CWORD]}" matches matchCount
# Collect matches, providing the current command-line token as input.
IFS=$'\n' read -d '' -ra matches <<<"$(compgen -A file "$currToken")"
# Count matches.
matchCount=${#matches[@]}
# Output in custom format, depending on the number of matches.
if (( matchCount > 1 && matchCount < 10 )); then
# Output matches in CUSTOM format:
# print the matches line by line, directly to the terminal.
printf '\n%s' "${matches[@]}" >/dev/tty
# !! We actually *must* pass out the current token as the result,
# !! as it will otherwise be *removed* from the redrawn line,
# !! even though $COMP_LINE *includes* that token.
# !! Also, by passing out a nonempty result, we avoid the bell
# !! signal that normally indicates a failed completion.
# !! However, by passing out a single result, a *space* will
# !! be appended to the last token - unless the compspec
# !! (mapping established via `complete`) was defined with
# !! `-o nospace`.
COMPREPLY=( "$currToken" )
# Finally, simulate redrawing the command line.
# Obtain an *expanded version* of `$PS1` using a trick
# inspired by /questions/283220/echo-expanded-ps1/1412737#1412737.
# !! This is NOT foolproof, but hopefully works in most cases.
expandedPrompt=$(PS1="$PS1" debian_chroot="$debian_chroot" "$BASH" --norc -i </dev/null 2>&1 | sed -n '${s/^\(.*\)exit$/\1/p;}')
printf '\n%s%s' "$expandedPrompt" "$COMP_LINE" >/dev/tty
else # Just 1 match or 10 or more matches?
# Perform NORMAL completion: let bash handle it by
# reporting matches via array variable `$COMPREPLY`.
COMPREPLY=( "${matches[@]}" )
fi
}
# Map the completion function (`_complete_foo`) to the command (`foo`).
# `-o nospace` ensures that no space is appended after a completion,
# which is needed for our workaround.
complete -o nospace -F _complete_foo -- foo