Подтвердить что ты не робот

Получение compgen для включения косой черты в каталоги при поиске файлов

Я хотел бы получить следующее поведение из моего пользовательского завершения

Учитывая

$ mkdir foo
$ touch foo faz/bar faz/baz

Я хотел бы получить это

$ foo -u <tab><tab> =>
foo faz/

$ foo -u fa<tab><tab> =>
foo -u faz/

$ foo -u faz/<tab><tab> =>
bar baz

Я предположил, что compgen -f f выводит foo faz/, но выводит foo faz, что не очень помогает мне.

Нужно ли выполнять пост-обработку вывода или есть какая-то волшебная комбинация параметров для компиляции, которые работают?

4b9b3361

Ответ 1

Я столкнулся с той же проблемой. Вот обходной путь, который я использую:

  • Зарегистрируйте функцию завершения с помощью -o default, например, complete -o default -F _my_completion.
  • Если вы хотите заполнить имя файла, просто установите COMPREPLY=() и включите Readline (что делает -o default).

У вас есть потенциальная проблема - вы можете использовать COMPREPLY=() для отказа в завершении, где это не подходит, и теперь это больше не будет работать. В Bash 4.0 и выше вы можете обойти это с помощью compopt следующим образом:

  • В верхней части вашей функции завершения всегда запускайте compopt +o default. Это отключает завершение имени файла Readline, когда COMPREPLY пуст.
  • Если вы хотите заполнить имя файла, используйте compopt -o default; COMPREPLY=(). Другими словами, включите чтение имени файла Readline только тогда, когда вам это нужно.

Я не нашел полного обходного пути для pre-4.0 Bash, но у меня есть что-то, что работает для меня достаточно хорошо. Я могу описать это, если кто-то действительно заботится, но, надеюсь, эти более старые версии Bash скоро все равно выпадут из общего использования.

Ответ 2

На это поведение влияет переменная readline mark-directories (и связанная с ней mark-symlinked-directories). Я считаю, что переменная должна быть установлена ​​ на для complete для печати конечной косой черты в именах каталогов (по умолчанию в bash v3.00.16). По-видимому, связанное поведение compgen не добавляет косой черты в имена каталогов: -\

Задайте значение mark-directories поочередно на on и off, затем повторите тест: -

bind 'set mark-directories on'
bind 'set mark-directories off'

Чтобы сделать изменение постоянным для будущих вызовов bash, добавьте следующее в ваш файл INPUTRC, обычно ~/.inputrc: -

$if Bash
# append '/' to dirnames
set mark-directories on
$endif

Совет для установки переменных readline в текущей оболочке был найден здесь: https://unix.stackexchange.com/a/27545. Я не определял, как проверить текущее значение переменной readline.

Дополнительные идеи

Возможно, только академический интерес...

Создайте список имен каталогов и добавьте косую черту: -

compgen -d -S / f

Ответ 3

Предыдущие ответы требуют bash 4 или не могут быть скомбинированы с другими дополнениями на основе процессоров для одной и той же команды. Следующее решение не требует compopt (поэтому оно работает с bash 3.2), и оно является составным (например, вы можете легко добавить дополнительную фильтрацию, чтобы соответствовать только определенным расширениям файлов). Продвигайтесь к концу, если вы нетерпеливы.


Вы можете протестировать compgen, запустив его непосредственно в командной строке:

compgen -f -- $cur

где $cur - это слово, которое вы набрали до сих пор во время интерактивного завершения табуляции.

Если я находится в каталоге с файлом afile.py и подкаталогом adir и cur=a, то приведенная выше команда показывает следующее:

$ cur=a
$ compgen -f -- $cur
adir
afile

Обратите внимание, что compgen -f показывает файлы и каталоги. compgen -d отображаются только каталоги:

$ compgen -d -- $cur
adir

Добавление -S / добавит конечную косую черту к каждому результату:

$ compgen -d -S / -- $cur
adir/

Теперь мы можем попробовать указать все файлы и все каталоги:

$ compgen -f -- $cur; compgen -d -S / -- $cur
adir
afile.py
adir/

Обратите внимание, что вам ничего не мешает вызывать compgen более одного раза! В вашем завершении script вы будете использовать его следующим образом:

cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(compgen -f -- "$cur"; compgen -d -S / -- "$cur") )

К сожалению, это все еще не дает нам такого поведения, которое мы хотим, потому что если вы наберете ad<TAB>, у вас есть два возможных завершения: adir и adir/. Итак, ad<TAB> завершится до adir, после чего вам все равно придется вводить / для устранения неоднозначности.

Теперь нам нужна функция, которая вернет все файлы, но не каталоги. Вот он:

$ grep -v -F -f <(compgen -d -P '^' -S '$' -- "$cur") \
>     <(compgen -f -P '^' -S '$' -- "$cur") |
>     sed -e 's/^\^//' -e 's/\$$//'
afile.py

Позвольте сломать его:

  • grep -f file1 file2 означает отображение строк в файле2, которые соответствуют любому шаблону в файле1.
  • -F означает, что шаблоны в файле1 должны точно совпадать (в качестве подстроки); они не являются регулярными выражениями.
  • -v означает инвертировать совпадение: показывать только строки, которые не находятся в файле1.
  • <(...) bash замена процесса. Он позволяет запускать любую команду в тех местах, где ожидается файл.

Итак, мы сообщаем grep: вот список файлов - удалите все, которые соответствуют этому списку каталогов.

$ grep -v -F -f <(compgen -d -P '^' -S '$' -- "$cur") \
>     <(compgen -f -P '^' -S '$' -- "$cur")
^afile.py$

Я добавил маркеры начала и конца с помощью compgen -P ^ и -S '$', потому что grep -F выполняет подстроку, и мы не хотим удалять имя файла, например a-file-with-adir-in-the-middle, только потому, что его средняя часть соответствует каталогу имя. Когда у нас есть список файлов, мы удаляем эти маркеры с помощью sed.

Теперь мы можем написать функцию, которая делает то, что мы хотим:

# Returns filenames and directories, appending a slash to directory names.
_mycmd_compgen_filenames() {
    local cur="$1"

    # Files, excluding directories:
    grep -v -F -f <(compgen -d -P ^ -S '$' -- "$cur") \
        <(compgen -f -P ^ -S '$' -- "$cur") |
        sed -e 's/^\^//' -e 's/\$$/ /'

    # Directories:
    compgen -d -S / -- "$cur"
}

Вы используете его следующим образом:

_mycmd_complete() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $(_mycmd_compgen_filenames "$cur") )
}
complete -o nospace -F _mycmd_complete mycmd

Обратите внимание, что -o nospace означает, что вы не получите пробел после каталога /. Для обычных файлов мы добавили пробел в конце с sed.

Одна хорошая вещь о том, что это в отдельной функции, - это то, что ее легко проверить! Например, здесь автоматизированный тест:

diff -u <(printf "afile.py \nadir/\n") <(_mycmd_compgen_filenames "a") \
    || { echo "error: unexpected completions"; exit 1; }

Ответ 4

Используйте опцию -o filenames, переданную в директиву complete. Пока содержимое COMPREPLY является допустимыми путями, косая черта будет добавляться соответствующим образом, в то же время добавляя пробел после завершения сопоставлений без каталогов. (Работает на Bash 3.2.)

_cmd() {
  COMPREPLY=( $( compgen -f -- "$cur" ))
  COMPREPLY=( $( compgen -A file -- "$cur" ))  # same thing
}

complete -o filenames -F _cmd cmd

См. info -n "(bash)Programmable Completion Builtins":

полная

  ...       -o COMP -o PTION   ...

  
     filenames
           Tell Readline that the compspec generates filenames, so
           it can perform any filename-specific processing (like
           adding a slash to directory names or suppressing
           trailing spaces).  This option is intended to be used
           with shell functions specified with '-F'.
  

И:

-A ACTION

  ...             "Файл"

  
           File names.  May also be specified as -f.
  

Примечание: compgen и complete имеют одинаковые аргументы.

Ответ 5

Это сработало для меня:

_my_completion() { 
  compopt -o nospace 
  COMPREPLY=( $(compgen -S "/" -d "${COMP_WORDS[$COMP_CWORD]}") )
}

complete -F _my_completion foo

Ответ 6

Если вы хотите продолжить использование вкладки после завершения работы каталога; здесь полное решение:

cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(compgen -S "/" -d -- ${cur}) ); 
compopt -o nospace;

сначала добавьте конечную косую черту в ответ. Затем запустите команду compopt -o nospace, чтобы удалить конечное пространство. Теперь, если у вас есть только foo/bar в вашем каталоге, для их ввода достаточно двух вкладок!