завершение bash для шаблонов имен файлов или каталогов

12

Я пытаюсь настроить скрипт завершения bash и у меня возникли проблемы.

Я хотел бы настроить его так, чтобы перечисленные дополнения были либо файлами, соответствующими определенному расширению, либо каталогами (которые могут содержать или не содержать файлы этого расширения).

Проблема, с которой я столкнулся, заключается в том, что единственный способ получить дополнения для хранения файлов и каталогов - использовать что-то вроде этого -o plusdirs -f -X '!*.txt', но когда я позволяю bash завершить один из каталогов, он просто добавляет пробел в конец, а не слэш.

_xyz()
{
  local cur=${COMP_WORDS[COMP_CWORD]}
  local prev=${COMP_WORDS[COMP_CWORD-1]}

  #COMPREPLY=( $( compgen -f -X '!*.txt' -- $cur ) )
  #COMPREPLY=( $( compgen -f -G '*.txt' -- $cur ) )
  #COMPREPLY=( $( compgen -o filenames -f -X '!*.txt' -- $cur ) )
  #COMPREPLY=( $( compgen -o dirnames  -f -X '!*.txt' -- $cur ) )
  COMPREPLY=( $( compgen -o plusdirs  -f -X '!*.txt' -- $cur ) )
  return 0
}

complete -F _xyz xyz

Я также перепробовал все закомментированные строки, но они даже не расширяют каталоги.

Для тестирования я запускал это в каталоге с одним файлом .txt и одним каталогом "dir" (с файлом внутри .txt, хотя это пока не имеет значения). Печатая xyz <TAB>с этой функцией перечисляет каталог и файл .txt, но печатая xyz d<TAB>расширяется до xyz dir(хорошо, с пробелом после "dir").

Роб I
источник

Ответы:

10

Если вы посмотрите на функцию _cd()в / etc / bash_completion , вы увидите, что она добавляет сам завершающий слеш, и что complete вызывается с опцией -o nospaceдля cd .

Вы можете сделать то же самое для xyz , но вам придется отдельно проверять, является ли найденное совпадение каталогом (если да, добавить косую черту) или файлом (если да, добавить пробел). Это должно быть сделано в цикле for для обработки всех найденных совпадений.

Кроме того, чтобы правильно обрабатывать пути, содержащие пробелы, вы должны установить внутренний разделитель файлов только на новую строку и экранировать пробелы. Использование IFS=$'\n'в сочетании с printf %qделает завершение работы практически со всеми персонажами. 1 Особая осторожность должна быть предпринята, чтобы не покинуть висячее пространство.

Следующее должно работать:

_xyz ()
{
    local IFS=$'\n'
    local LASTCHAR=' '

    COMPREPLY=($(compgen -o plusdirs -f -X '!*.txt' \
        -- "${COMP_WORDS[COMP_CWORD]}"))

    if [ ${#COMPREPLY[@]} = 1 ]; then
        [ -d "$COMPREPLY" ] && LASTCHAR=/
        COMPREPLY=$(printf %q%s "$COMPREPLY" "$LASTCHAR")
    else
        for ((i=0; i < ${#COMPREPLY[@]}; i++)); do
            [ -d "${COMPREPLY[$i]}" ] && COMPREPLY[$i]=${COMPREPLY[$i]}/
        done
    fi

    return 0
}

complete -o nospace -F _xyz xyz

1 Символ новой строки здесь является очевидным исключением, поскольку он является внутренним разделителем файлов.

Деннис
источник
Это прекрасно работает (хотя это очень плохо, это не встроено). Благодарность!
Роб I
1
Любая причина не использовать «$ 2» вместо «$ {COMP_WORDS [COMP_CWORD]}»?
Эдвард Фальк
3

Я думаю, что это простое решение работает в том, что оно:

  1. Соответствует каталогам и файлам, которые заканчиваются на .txt
  2. Обрабатывает пробелы в именах файлов
  3. Добавляет косую черту в конце папки без пробела
  4. Добавляет место в конце соответствия файла

Ключ передавался -o filenamesдля завершения. Это было проверено на GNU bash 3.2.25 на RHEL 5.3 и GNU bash 4.3.18 на osx

_xyz()
{
  local cur=${COMP_WORDS[COMP_CWORD]}

  local IFS=$'\n'
  COMPREPLY=( $( compgen -o plusdirs  -f -X '!*.txt' -- $cur ) )
}

complete -o filenames -F _xyz xyz
Чад Скитерс
источник
Да, похоже, отлично работает. Гораздо проще, спасибо, что поймали важный аргумент!
Роб I