conclusão do bash para padrões ou diretórios de nome de arquivo

12

Estou tentando obter um script de conclusão do bash e tendo alguns problemas.

Gostaria de configurá-lo para que as conclusões listadas sejam arquivos correspondentes a uma extensão específica ou diretórios (que podem ou não conter arquivos dessa extensão).

O problema que estou tendo é que a única maneira de obter as conclusões para conter arquivos e diretórios é usando algo como -o plusdirs -f -X '!*.txt', mas quando deixo o bash concluir um dos diretórios, ele apenas adiciona um espaço ao final, em vez de um golpear.

_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

Também tentei todas as linhas comentadas, mas elas nem expandem os diretórios.

Para testar, eu tenho executado isso em um diretório com um arquivo .txt e um diretório "dir" (com um arquivo .txt dentro dele, embora isso ainda não importe). Digitar xyz <TAB>com esta função lista o diretório e o arquivo .txt, mas a digitação se xyz d<TAB>expande para xyz dir(bem, com um espaço após "dir").

Rob I
fonte

Respostas:

10

Se você olhar para a função _cd()em / etc / bash_completion , você verá que ele acrescenta o barra final em si e que completa obtém chamado com a opção -o nospacede cd .

Você pode fazer o mesmo com o xyz , mas precisa verificar separadamente se a correspondência encontrada é um diretório (se houver, acrescentar barra) ou um arquivo (se houver, acrescentar espaço). Isso deve ser feito em um loop for para processar todas as correspondências encontradas.

Além disso, para manipular adequadamente os caminhos que contêm espaços, você deve definir o separador de arquivo interno como somente nova linha e escapar dos espaços. Usar IFS=$'\n'em combinação com printf %qfaz a conclusão funcionar com quase todos os caracteres. 1 Cuidados especiais devem ser tomados para não escapar do espaço de fuga.

O seguinte deve funcionar:

_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 O caractere de nova linha é a exceção óbvia aqui, pois é um separador interno de arquivos.

Dennis
fonte
Isso funciona muito bem (embora seja uma pena que não esteja embutido). Obrigado!
que você
1
Algum motivo para não usar "$ 2" em vez de "$ {COMP_WORDS [COMP_CWORD]}"?
Edward Falk
3

Penso que esta solução simples funciona na medida em que:

  1. Corresponde a diretórios e arquivos que terminam em .txt
  2. Manipula espaços nos nomes dos arquivos
  3. Adiciona uma barra no final das conclusões da pasta sem espaço à direita
  4. Adiciona espaço no final de uma correspondência de conclusão de arquivo

A chave estava passando -o filenamespara ser concluída. Isso foi testado no GNU bash 3.2.25 no RHEL 5.3 e GNU bash 4.3.18 no 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
Chade Skeeters
fonte
Sim, isso parece funcionar muito bem. Muito mais simples, obrigado por entender o argumento importante!
que você