Найти автозаполнение имени файла в скрипте Bash
Есть функция командной строки, о которой я давно мечтал, и я подумал, как лучше всего ее реализовать, но у меня ничего нет...
Так что я хотел бы иметь, когда я начинаю вводить имя файла и нажимать вкладку, например:
# git add Foo<tab>
Я хотел бы запустить find . -name "*$1*"
и в основном автозаполнение полного пути к соответствующему файлу в моей командной строке.
Что у меня так далеко:
Я знаю, что мне придется написать функцию, которая будет вызывать приложение с параметрами, которые я хочу, например git add
, После этого необходимо перехватить событие нажатия клавиши и выполнить поиск, упомянутый выше, и отобразить результаты, если их много, или заполнить результат, если таковой имеется.
Что я не смог понять:
Как перехватить событие клавиши табуляции внутри функции внутри функции.
Так что в основном в псевдокоде:
gadd() {git add autocomplete_file_search($1)}
autocomplete_file_search(keyword) {
if( tab-key-pressed ){
files = find . -name "*$1*";
if( filecount > 1 ) {
show list;
}
if( files == 1 ) {
return files
}
}
}
Есть идеи?
Благодарю.
4 ответа
Вы должны взглянуть на это введение для завершения bash. Вкратце, в bash есть система для настройки и расширения вкладок. Это делают и другие оболочки, и у каждого есть свой способ настройки. При использовании этой системы нет необходимости делать все самостоятельно, и добавить завершение пользовательского аргумента в команду относительно просто.
Совпадение в любом месте имени файла довольно сложно, и я не уверен, что это действительно так полезно. Сопоставление в начале имен файлов имеет больше смысла и намного проще в реализации, даже рекурсивно.
Теперь, вы упомянули find как требование, но bash (начиная с версии 4.0) также может рекурсивно находить файлы, и это должно быть более эффективным, если bash выполняет эту часть. Для рекурсивного соответствия в bash вы включаете опцию оболочки globstar, выполнив shopt -s globstar
затем две последовательные звездочки, **
, будет соответствовать рекурсивно.
Далее, учитывая, что вы хотите рекурсивно сопоставлять файлы в репозитории git, у нас есть лучший способ обнаружить, что мы на самом деле в репозитории git, в противном случае, если вы случайно запустите его в /
например, ваш запрос будет зависать при ожидании bash для поиска по всей вашей файловой системе. Следующая функция должна быть достаточно эффективной для определения, находимся ли мы в git-репозитории. Учитывая текущий рабочий каталог, например /foo/bar/baz
поищу /foo/bar/baz/.git
, /foo/bar/.git
, /foo/.git
, /.git
и верните true, если он найдет один, иначе false.
isgit() {
local p=$PWD
while [[ $p ]]; do
[[ -d $p/.git ]] && return
p=${p%/*}
done
return 1
}
Для простоты мы создадим gadd
Команда для добавления дополнений для. Функция завершения может быть применена только к первому слову команды. Например, мы можем добавить завершение для git
, но нет git add
Таким образом, мы сделаем новую команду, которая превращает git add
в одно слово.
gadd() {
git add "$@"
}
Теперь для фактического завершения функции. При срабатывании при нажатии клавиши TAB функция будет вызываться с тремя аргументами. $1
команда завершается, $2
текущее слово завершаемой командной строки, и $3
предыдущее слово в строке. Таким образом, файлы, которые мы хотим найти, будут сопоставлены глобусом **/"$2"*
; все файлы, начинающиеся с "$2"
, Мы повторяем эти имена файлов и добавляем их в массив COMPREPLY. Если массив COMPREPLY содержит только одно значение после завершения функции, слово будет заменено этим значением. Если оно содержит более одного значения, нажмите вкладку в другой раз, чтобы получить список всех совпадений.
shopt -s globstar
_git_add_complete() {
local file
isgit || return
for file in **/"$2"*; do
# If the glob doesn't match, we'll get the glob itself, so make sure
# we have an existing file
[[ -e $file ]] || continue
# If it's a directory, add a trailing /
[[ -d $file ]] && file+=/
COMPREPLY+=( "$file" )
done
}
complete -F _git_add_complete gadd
Добавьте вышеупомянутые три блока кода к вашему ~/.bashrc
, затем откройте новый терминал, войдите в репозиторий git и попробуйте gadd something<tab>
,
Это работает?
$ cat .bash_completion
_foo()
{
local files
cur=${COMP_WORDS[COMP_CWORD]}
local files=$(for x in `find -type f`; do echo ${x}; done)
COMPREPLY=( $( compgen -W "${files}" -- ${cur} ) )
return 0
}
complete -F _foo foo
$ . /etc/bash_completion
$ foo ./[tab]
Я написал git-number, чтобы мне никогда не приходилось нажимать на вкладку при указании файлов для git.
С помощью git-number я могу использовать числа для представления имен файлов, которые я хочу обработать с помощью git.