Подкаталог автозаполнения Bash для пользовательских команд

462
Gedrick Wilson

Я часами пытался заставить это работать и чувствую, что не приблизился.

Я пытаюсь сократить наш рабочий процесс, заполнив табуляцию имен каталогов из Mac OS X Terminal.

У нас есть внутренний инструмент, давайте назовем его bob, который автоматически запускает команды на подмодулях git. То, что я хотел бы сделать, это заставить его автоматически заполнять имена подкаталогов. Проблема в том, что эти подкаталоги живут в статическом месте.

 project directory ->public ->submodules ->a-module ->another-module ->other-module ->some-module 

Команды запускаются из верхнего каталога проекта следующим образом:

bob start bugfix anothermodule 

Вместо того, чтобы вводить «другой модуль» полностью, что мы и делаем в настоящее время, я бы хотел, чтобы автозаполнение вкладки работало. Мы используем этот рабочий процесс много раз в день, и было бы здорово сэкономить несколько секунд и избежать опечаток.

Конечная цель:

bob start bugfix an<tab> 

будет автозаполнение до

bob start bugfix anothermodule 

Я пытался, CDPATHно быстро понял, что это не то, что мне нужно, так как это не после cdкоманды, а только моя команда.

Я bash_completionустановил, но я не очень хорош в Bash в целом. Упоминание об этом, хотя в случае, если это может быть частью решения!

2

2 ответа на вопрос

1
Kamil Maciorowski

Это верно, bash_completionэто решение. Я могу рассказать вам, что у меня есть в моей Ubuntu. Я надеюсь, что на Mac все по крайней мере похоже.

Есть /etc/bash_completionфайл, источники которого /usr/share/bash-completion/bash_completion.

В каталоге /usr/share/bash-completionтакже есть completionsподкаталог. Это место для сценариев. Давайте посмотрим на ejectсценарий:

# bash completion for eject(1) -*- shell-script -*-  _eject() { local cur prev words cword _init_completion || return  case $prev in -h|--help|-V|--version|-c|--changerslot|-x|--cdspeed) return ;; -a|--auto|-i|--manualeject) COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) ) return ;; esac  if [[ $cur == -* ]]; then COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "$cur" ) ) return elif [[ $prev == @(-d|--default) ]]; then return fi  _cd_devices _dvd_devices } && complete -F _eject eject  # ex: ts=4 sw=4 et filetype=sh 

Даже не читая документацию, мы могли бы немного переработать ее и приспособить к вашему случаю. Для нас самая важная часть кода выше, где он говорит, что после того -a, --auto -iили --manualejectможет быть onили off. В вашем случае после bugfixвам нужно имя каталога.

(Или нет. У вас есть another-moduleкаталог, но вы используете anothermoduleаргумент. Пока я предполагаю, что это опечатка. Если это не так, то ответ Энтони, я думаю, укажет вам правильное направление.)

Мы могли бы как-то извлечь эти каталоги, которые вы хотите, и заменить 'on off', но может быть лучше использовать существующий код. После того, как я печатаю cd, bashautocompletes каталоги, и она работает хорошо, даже если есть пробелы или специальные символы. Итак, давайте посмотрим на _cd()функцию; это внутри /usr/share/bash-completion/bash_completion, я не буду вставлять это здесь. Затем кажется, что ключевой частью этой функции является _filedir -dвызов. Непосредственно перед тем, _filedir()как определено, мы имеем:

# This function performs file and directory completion. It's better than # simply using 'compgen -f', because it honours spaces in filenames. 

Бинго.

Поэтому я скопировал ejectскрипт под именем bobи отредактировал в виде:

# bash completion for bob -*- shell-script -*-  _bob() { local cur prev words cword curdir _init_completion || return  case $prev in bob) COMPREPLY=( $( compgen -W 'start stop foo bar' -- "$cur" ) ) return ;; start) COMPREPLY=( $( compgen -W 'bugfix' -- "$cur" ) ) return ;; bugfix) curdir=$(pwd) cd public/submodules/ 2>/dev/null && _filedir -d cd "$curdir"  ;; esac  } && complete -F _bob bob 

Существующие bashсеансы не заметят этот файл, вы должны запустить новый, bashчтобы протестировать его.

Да, это быстрое и грязное программирование вуду, извините.

Обратите внимание, что мы спускаемся public/submodules/и возвращаемся. Обычно я бы использовал подоболочку:

export -f _filedir ( cd … && _filedir -d ) 

но похоже, что так _filedirне получится.

Отличный ответ: мне нравится, как вы модифицируете уже существующую функцию завершения. Он делает то, что хочет ОП - кроме удаления дефиса. Я видел некоторые программные проекты, в которых имя компонента не совпадает с соответствующим именем в файловой системе, поэтому оно может не быть опечаткой. Поскольку комментарии не подходят для кодовых блоков, я разместил свои модификации как [отдельный ответ] (https://superuser.com/a/1269950/247588). Anthony Geoghegan 6 лет назад 0
@AnthonyGeoghegan Вау! Спасибо. Kamil Maciorowski 6 лет назад 0
Пожалуйста. Некоторое время назад я следовал нескольким учебникам, чтобы реализовать некоторые базовые пользовательские дополнения, используя `compgen` и друзей, но я не слишком зашел с этим. Я нашел ваш подход к взлому функций, предоставляемых `bash_completion`, образовательным и практичным. Anthony Geoghegan 6 лет назад 0
1
Anthony Geoghegan

Это расширение отличного ответа Камиля, которое удаляет дефис из -moduleчасти имени каждого подкаталога.

bugfix) curdir=$(pwd) cd public/submodules/ 2>/dev/null && _filedir -d # Strip the hyphen from `-module` in each sub-directory name. for ((i=0; i<${#COMPREPLY[@]}; i++)); do COMPREPLY[$i]="$" done cd "$curdir" ;; 

Есть еще возможности для улучшения этого раздела: вам, возможно, придется ввести дефис, т. Е. Набрать, a-Tabчтобы завершить его, amoduleвместо выбора между amoduleи anothermodule(дефис по-прежнему удаляется функцией завершения).

Я думаю, что если вы замените `COMPREPLY = ($ (compgen -d -" $ cur "))` просто на `_filedir -d`, тогда ваш код будет лучше обрабатывать имена каталогов с пробелами. Kamil Maciorowski 6 лет назад 0
Большое спасибо @KamilMaciorowski Я так долго использовал пакет `bash-complete`, что думал, что обработка пространства встроена в` compgen`. Anthony Geoghegan 6 лет назад 0