Criar script de conclusão do bash para preencher automaticamente caminhos após o sinal de igual?

11

Eu quero criar um script de conclusão do bash que reconheça argumentos do formulário --arge --some-arg=file.

Depois de ler este tutorial e alguns exemplos /usr/share/bash_completion/completions/, escrevi o seguinte script (para economizar tempo ao digitar alguns sinalizadores com o Chromium):

_chromium() 
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    # Some interesting options
    opts="
--disable-web-security
--easy-off-store-extension-install
--incognito
--load-extension=
--pack-extension=
--pack-extension-key=
--user-data-dir=
"
    # Handle --xxxxxx=file
    if [[ ${cur} == "--"*"=" ]] ; then
        # Removed failures (is my logic OK?)
        return 0
    fi

    # Handle other options
    if [[ ${cur} == -* ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
        return 0
    fi
}
complete -F _chromium chromium

Salvei ~/bash_completions/chromiume criei um link simbólico usando sudo ln -s ~/bash_completions/chromium /usr/share/bash_completion/completions/chromium.
Então eu carreguei usando . /usr/share/bash_completions/completions/chromium.

Agora, estou com dois problemas:

  • chromium --u<TAB>expande para chromium --user-data-dir=<SPACE>(eu não quero o espaço).
  • Caminhos (diretórios e arquivos) não são mais concluídos.

Como posso resolver esses problemas?

Rob W
fonte
A mudança para o zsh é uma opção?
Gilles 'SO- stop be evil'

Respostas:

11

Eu encontrei a solução para ambos os problemas!

  1. Para não acrescentar um espaço, use a nospaceopção Isso pode ser feito de duas maneiras:

    • Passe para o complete:
      complete -o nospace -F _chromium chromium
    • Use o compoptbuilt-in:
      compopt -o nospace(para ativar a opção)
      compopt +o nospace(para desativar a opção)

    Encontrei-o na documentação do Bash em gnu.org, 8.7 Construções programáveis ​​de conclusão .

  2. Conclusão de arquivos.
    • Peter sugeriu usar a -fbandeira com compgen. Eu segui esse conselho e o implementei como COMPREPLY=( $(compgen -f "$cur") ). Isso funcionou bem, até que eu tentei concluir um caminho contendo espaços.
      No Stack Overflow, encontrei esta resposta que recomenda a criação manual da lista de conclusão (sem usar compgen). Essa abordagem funcionou bem.
    • Use a filenamesopção para informar ao Readline que o compspec gera nomes de arquivos, para que ele possa:
      • execute qualquer processamento específico de nome de arquivo (como adicionar uma barra aos nomes de diretório, citar caracteres especiais ou suprimir espaços à direita)
      • use cores diferentes para indicar o tipo de arquivo (com colored-statsativado)

Com a ajuda da manipulação de cadeias de caracteres de Bash (para expandir ~e lidar com espaços), construí scripts de conclusão do bash que atendem a todos os critérios estabelecidos na pergunta.

_chromium() 
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    # Some interesting options
    opts="
--disable-web-security
--easy-off-store-extension-install
--incognito
--load-extension=
--pack-extension=
--pack-extension-key=
--user-data-dir=
"
    # Handle --xxxxxx=
    if [[ ${prev} == "--"* && ${cur} == "=" ]] ; then
        compopt -o filenames
        COMPREPLY=(*)
        return 0
    fi
    # Handle --xxxxx=path
    if [[ ${prev} == '=' ]] ; then
        # Unescape space
        cur=${cur//\\ / }
        # Expand tilder to $HOME
        [[ ${cur} == "~/"* ]] && cur=${cur/\~/$HOME}
        # Show completion if path exist (and escape spaces)
        compopt -o filenames
        local files=("${cur}"*)
        [[ -e ${files[0]} ]] && COMPREPLY=( "${files[@]// /\ }" )
        return 0
    fi

    # Handle other options
    COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
    if [[ ${#COMPREPLY[@]} == 1 && ${COMPREPLY[0]} != "--"*"=" ]] ; then
        # If there's only one option, without =, then allow a space
        compopt +o nospace
    fi
    return 0
}
complete -o nospace -F _chromium chromium
Rob W
fonte
Resposta incrível, muito obrigado. Você economizou muito do meu tempo. Eu usei o -fswitch para concluir o caminho do arquivo, mas ele não conclui o /char se for um diretório ... mas é uma coisa pequena.
Jiri Kremser
1

Para completar os nomes de arquivos, tente passar -fpara compgen.

Receio que você não consiga se livrar dos espaços após as opções, pois é assim que a conclusão funciona - uma vez que encontra uma correspondência única, ela a completa completamente.

peterph
fonte
Obrigado pela sugestão de -f. Eu tentei, mas ele não lida corretamente com espaços: O caminho /tmp/foo barresulta em duas entradas de conclusão /tmp/fooe bar. Sobre os indesejados --arg=<SPACE>: Encontrei a solução para isso e postei na resposta abaixo .
Rob W