Найти автозаполнение имени файла в скрипте 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.

Другие вопросы по тегам