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").

2 ответа2

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 Символ новой строки здесь является очевидным исключением, поскольку он является внутренним разделителем файлов.

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .