Como posso o bash executar o preenchimento de guias para meus aliases?


45

Eu tenho um monte de scripts de conclusão do bash configurados (principalmente usando o bash-it e algumas configurações manualmente).

Eu também tenho um monte de aliases configurados para tarefas comuns como gcopara git checkout. No momento, eu posso digitar git checkout dTabe developestá completo para mim, mas quando digito, gco dTabele não está completo.

Estou assumindo que isso ocorre porque o script de conclusão está sendo concluído gite ele falha ao ver gco.

Existe uma maneira de obter generosa / programaticamente todos os meus scripts de conclusão para trabalhar com meus aliases? Não poder concluir quando o tipo de alias é derrotado pelo objetivo do alias.


Qual SO e bash você está usando? Estou no Ubuntu 11.10 e libero o bash 4.2.10 (1) (x86_64-pc-linux-gnu) e tenho essa funcionalidade embutida no meu shell para os meus muitos pseudônimos. btw bash --versionpara obter isso (não use -v, saída diferente).
precisa

Desculpe, perdi algumas informações - OSX Lion, GNU bash, versão 3.2.48 (1) -release (x86_64-apple-darwin11)
dstarh

1
@ killmist: a menos que eu esteja completamente enganado, o zsh também não conclui os comandos com alias. Implementar uma função que adiciona aliases definidos à conclusão parece ser muito mais fácil do que para o bash, no entanto, como o sistema de conclusão do zhs parece mais poderoso e mais direto do que o bash.
22612 kopischke


1
@MichaelDurrant Tem certeza de que isso é realmente construído para aliases? Estou no Ubuntu 15.10 com o Bash 4.3.42 (1) -release (x86_64-pc-linux-gnu) e não existe tal coisa. Eu também testei alguns lançamentos anteriores. Por exemplo, se você digitar ll --[TAB], imprimirá uma lista de opções para ls? Sou bastante cético quanto a isso, mas se você tem certeza de que tal coisa existia na 11.10, ficaria curioso para investigar e determinar o que foi removido.
6

Respostas:


42

O código a seguir, adaptado desta resposta do Stack Overflow e deste tópico de discussão dos Fóruns do Ubuntu , adicionará conclusões para todos os seus aliases definidos:

# Automatically add completion for all aliases to commands having completion functions
function alias_completion {
    local namespace="alias_completion"

    # parse function based completion definitions, where capture group 2 => function and 3 => trigger
    local compl_regex='complete( +[^ ]+)* -F ([^ ]+) ("[^"]+"|[^ ]+)'
    # parse alias definitions, where capture group 1 => trigger, 2 => command, 3 => command arguments
    local alias_regex="alias ([^=]+)='(\"[^\"]+\"|[^ ]+)(( +[^ ]+)*)'"

    # create array of function completion triggers, keeping multi-word triggers together
    eval "local completions=($(complete -p | sed -Ene "/$compl_regex/s//'\3'/p"))"
    (( ${#completions[@]} == 0 )) && return 0

    # create temporary file for wrapper functions and completions
    rm -f "/tmp/${namespace}-*.tmp" # preliminary cleanup
    local tmp_file; tmp_file="$(mktemp "/tmp/${namespace}-${RANDOM}XXX.tmp")" || return 1

    local completion_loader; completion_loader="$(complete -p -D 2>/dev/null | sed -Ene 's/.* -F ([^ ]*).*/\1/p')"

    # read in "<alias> '<aliased command>' '<command args>'" lines from defined aliases
    local line; while read line; do
        eval "local alias_tokens; alias_tokens=($line)" 2>/dev/null || continue # some alias arg patterns cause an eval parse error
        local alias_name="${alias_tokens[0]}" alias_cmd="${alias_tokens[1]}" alias_args="${alias_tokens[2]# }"

        # skip aliases to pipes, boolean control structures and other command lists
        # (leveraging that eval errs out if $alias_args contains unquoted shell metacharacters)
        eval "local alias_arg_words; alias_arg_words=($alias_args)" 2>/dev/null || continue
        # avoid expanding wildcards
        read -a alias_arg_words <<< "$alias_args"

        # skip alias if there is no completion function triggered by the aliased command
        if [[ ! " ${completions[*]} " =~ " $alias_cmd " ]]; then
            if [[ -n "$completion_loader" ]]; then
                # force loading of completions for the aliased command
                eval "$completion_loader $alias_cmd"
                # 124 means completion loader was successful
                [[ $? -eq 124 ]] || continue
                completions+=($alias_cmd)
            else
                continue
            fi
        fi
        local new_completion="$(complete -p "$alias_cmd")"

        # create a wrapper inserting the alias arguments if any
        if [[ -n $alias_args ]]; then
            local compl_func="${new_completion/#* -F /}"; compl_func="${compl_func%% *}"
            # avoid recursive call loops by ignoring our own functions
            if [[ "${compl_func#_$namespace::}" == $compl_func ]]; then
                local compl_wrapper="_${namespace}::${alias_name}"
                    echo "function $compl_wrapper {
                        (( COMP_CWORD += ${#alias_arg_words[@]} ))
                        COMP_WORDS=($alias_cmd $alias_args \${COMP_WORDS[@]:1})
                        (( COMP_POINT -= \${#COMP_LINE} ))
                        COMP_LINE=\${COMP_LINE/$alias_name/$alias_cmd $alias_args}
                        (( COMP_POINT += \${#COMP_LINE} ))
                        $compl_func
                    }" >> "$tmp_file"
                    new_completion="${new_completion/ -F $compl_func / -F $compl_wrapper }"
            fi
        fi

        # replace completion trigger by alias
        new_completion="${new_completion% *} $alias_name"
        echo "$new_completion" >> "$tmp_file"
    done < <(alias -p | sed -Ene "s/$alias_regex/\1 '\2' '\3'/p")
    source "$tmp_file" && rm -f "$tmp_file"
}; alias_completion

Para aliases simples (apenas comando, sem argumentos), ele atribuirá a função de conclusão original ao alias; para aliases com argumentos, ele cria uma função de wrapper que insere os argumentos extras na função de conclusão original.

Diferentemente dos scripts dos quais ele evoluiu, a função respeita aspas tanto para o comando alias quanto para seus argumentos (mas o primeiro precisa corresponder ao comando de conclusão e não pode ser aninhado) e deve filtrar com segurança aliases para listas de comandos e pipes (que são ignorados, pois é impossível descobrir o que concluir neles sem recriar a lógica completa de análise de linha de comando do shell).

Uso

Salve o código como um arquivo de script de shell e origine- o ou copie a função atacada para .bashrc(ou seu arquivo de ponto pertinente ). O importante é chamar a função depois que as definições de conclusão e alias do bash tiverem sido configuradas (o código acima chama a função logo após a sua definição, no espírito "fonte e esqueça", mas você pode mover a chamada para qualquer lugar a jusante, se isso combina melhor com você). Se você não quiser a função em seu ambiente após a saída, poderá adicioná-la unset -f alias_completionapós chamá-la.

Notas

Se você estiver usando o bash4.1 ou superior e usar conclusões carregadas dinamicamente, o script tentará carregar conclusões para todos os comandos com alias, para que ele possa criar as funções do wrapper para seus alias.


1
Como eu instalaria esse script?
Der Hochstapler

1
@ OliverSalzburg: você terá que processá-lo em um dos seus arquivos de perfil do shell, crucialmente após a conclusão do bash - que provavelmente o faria ~/.bashrc. Armazene-o como um arquivo de script de shell e origine-o ( . /path/to/alias_completion.sh) ou copie e cole o código por atacado.
22412 kopischke #

1
@ OliverSalzburg: instruções de uso adicionadas (não percebemos imediatamente que você não é o OP).
Kopischke

1
@kopischke Veja esta pergunta - aparentemente para os arquivos em que /usr/share/bash-completion/completions/eles são carregados apenas na primeira vez em que o usuário realmente acessa [TAB]. Portanto, mesmo que a função seja carregada, ~/.bashrcela não gerará conclusões para aliases para comandos nela. Após assegurar complete -pestá trabalhando para apt-gete apt-cacheeu copiar-colar a sua função para o terminal e ele está funcionando corretamente.
Jamadagni

1
@kopischke Então, não sei como forçar o fornecimento de todos os arquivos de conclusão carregados dinamicamente, ou mesmo se for aconselhável. Por enquanto, copiei o arquivo de conclusão gerado de /tmppara ~/.bash_completione adicionei manualmente, no início, as source /usr/share/bash-completion/completions/entradas relevantes (separadamente para apt-gete apt-cache- apt-{cache,get}não funciona).
Jamadagni

4

Existe uma maneira de obter generosa / programaticamente todos os meus scripts de conclusão para trabalhar com meus aliases?

Sim, aqui está o projeto de alias completo que resolve seu problema exatamente. Ele fornece a conclusão de alias genérica e programática sem o uso eval.


2

Esta é a maneira manual, para quem está procurando por isso.

Primeiro, procure o comando de conclusão original. Exemplo:

$ complete | grep git

complete -o bashdefault -o default -o nospace -F __git_wrap__git_main git

Agora adicione-os ao seu script de inicialização (por exemplo, ~ / .bashrc):

# load dynamically loaded completion functions (may not be required)
_completion_loader git

# copy the original statement, but replace the last command (git) with your alias (g)
complete -o bashdefault -o default -o nospace -F __git_wrap__git_main g

fonte: https://superuser.com/a/1004334

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.