Thank you for reading this post, don't forget to subscribe!
Анатомия завершения программы
Оказывается, завершение зависит от конкретной библиотеки GNU, известной как readline. Он читает текст для множества различных программ, включая Bash, и вы можете настроить его, используя файл .inputrc в вашем домашнем каталоге. Например, вот пример.inputrc :
1 2 3 4 5 6 7 8 9 |
"\e[A": history-search-backward "\e[B": history-search-forward $if Bash Space: magic-space $endif set match-hidden-files off set completion-ignore-case on set visible-stats on set show-all-if-ambiguous on |
Это не выглядит много, но есть системная конфигурация в /etc/inputrc, которая намного более существенна. Вы также можете выполнить определенные команды, которые будут изменять конфигурацию на лету. Например, встроенный в Bash «bind» может настроить все, а также покажет вам длинный список того, что уже настроено. Если вы делаете эту команду:
1 |
bind -p |
Вы увидите длинный список результатов. Интересны следующие несколько строк:
1 2 3 4 5 6 7 8 |
"\C-i": complete "\e\e": complete "\e!": complete-command "\e/": complete-filename "\e@": complete-hostname "\e{": complete-into-braces "\e~": complete-username "\e$": complete-variable |
Ваш может отличаться, но, по сути, это говорит о том, что, когда появляется клавиша табуляции или двойной выход, выполнить операцию завершения. Есть также несколько специализированных дополнений, которые начинаются с побега.
Но как это работает?
Есть три встроенные команды оболочки, которые взаимодействуют, чтобы управлять завершением, это:
- complete – настроить завершение
- compgen – генерирует возможные дополнения
- compopt – Изменить параметры завершения
Мы не будем вдаваться во все возможные варианты, хотя вы можете прочитать руководство по Bash, если вам интересно. Для наших целей вам понадобится всего несколько, и все три команды в большинстве случаев принимают одинаковые аргументы.
Давайте попробуем несколько вещей с compgen, так как это то, что bash в конце концов вызывает для завершения. Предположим, вы набираете команду ls в командной строке и нажимаете tab. Bash сделает следующий вывод:
1 |
compgen -c ls |
Вы увидите вывод (в зависимости от того, что вы установили) всех команд, начинающихся с ls , таких как ls , lsusb , lspci и т. д. Вы также можете использовать более современный синтаксис «-A command» для получения того же результата.
Попробуйте это:
1 |
compgen -d / e |
Опять же, вы можете использовать каталог -A, если хотите. Понятно, что это выводит каталоги. Это хорошо, но как Bash узнает, какие опции нужно передать в compgen? Вот где приходит команда complete. Команда complete может указать Bash вызвать функцию оболочки или команду в ответ на завершение выполнения определенной команды. Он также может установить один для пустой команды или команды, у которой иначе нет соответствующего правила. Есть также хорошие ярлыки для общих случаев.
Рассмотрим скрипт под названием burnhex. Если вы введете Burnhex , пробел и вкладку, Bash (через readline ) предложит вам все файлы в текущем каталоге для завершения. Однако вы можете догадаться, что вам нужны только файлы, заканчивающиеся на .hex. Вы можете легко сделать с помощью complete:
1 |
complete -G "*.hex" burnhex |
Теперь будут отображаться только файлы с расширением .hex.
Чтобы взглянуть на еще более сложные дополнения, давайте начнем с простой функции оболочки. По соглашению, они начинаются с подчеркивания, хотя это не техническое требование:
1 2 3 4 |
function _andreyex { COMPREPLY=( "andreyex.ru" "andreyex.io" ) return 0 } |
Это говорит системе, что у нас есть два дополнения. Давайте представим, что у нас есть скрипт или программа под названием « goandreyex », и мы хотим, чтобы это было завершением (команда не должна существовать, чтобы это работало):
1 |
complete -F _andreyex goandreyex |
Теперь, если вы введете:
1 |
goandreyex<SPACE><TAB> |
Вы увидите свое завершение в действии. Система достаточно умна, чтобы знать, что она может заполнить часть «andreyex», поскольку оба варианта начинаются с этого. Если вы наберете «c» или «i» и снова нажмете «Tab», он выберет правильный вариант.
Кстати, функция получает три аргумента, которые мы не используем. Первый аргумент ($ 1) – это имя активной команды, второй – завершаемое слово, а третий – слово перед текущим словом. Например, рассмотрим строку ниже и три аргумента следующие:
1 2 3 4 |
ls -l /foo $1=ls $2=/foo $3=-l |
Также есть несколько переменных среды, которые вы хотите установить, если хотите извлечь всю строку или определить, как пользователь вызвал завершение. Для простых случаев вам, вероятно, это не нужно.
Кроме того, помните опции -c и -d для compgen ? Они тоже работают здесь. Так что если у вас есть команда с именем foobar, для которой в качестве аргумента нужно указать имя каталога, вы можете сказать:
1 |
complete -d foobar |
Вы даже можете вызвать compgen в своей функции или команде, чтобы генерировать данные напрямую или для дальнейшей фильтрации и дополнения в вашем коде. Это может быть довольно сложно, и если вы посмотрите на некоторые из встроенных, вы поймете, что мы имеем в виду.
В целом, многие сценарии завершения находятся либо в /etc/bash_completion.d или /usr/share/bash-completions. Да, один из них – подчеркивание, а другой – тире. Одна из радостей Linux заключается в том, что каждая система немного отличается.
Более типичный пример
Рассмотрим команду ftp. Вы можете узнать, что это за завершение, набрав:
1 |
complete -p | grep ftp |
Результат будет выглядеть так:
1 |
complete -F _known_hosts ftp |
Это означает, что есть функция оболочки с именем _known_hosts. Если вы хотите увидеть, как это выглядит, попробуйте:
1 |
declare -f _known_hosts |
Вы должны увидеть что-то вроде этого:
1 2 3 4 5 6 7 8 9 |
_known_hosts () { local cur prev words cword; _init_completion -n : || return; local options; [[ "$1" == -a || "$2" == -a ]] && options=-a; [[ "$1" == -c || "$2" == -c ]] && options+=" -c"; _known_hosts_real $options -- "$cur" } |
Очевидно, что настоящая работа выполняется в _known_hosts_real, и вы можете сбросить ее таким же образом. Это сложно, но вы можете догадаться, что он будет сбрасывать имена хостов, о которых знает компьютер. Есть и другие вспомогательные функции, такие как _init_completion . Дело не в том, как они работают, а в том, что они существуют, и вы можете использовать их так же, как это делает установка завершения ftp.