Como obtenho a conclusão do bash para trabalhar com aliases?

195

Caso em questão:

Eu sou um no mac com o bash v3.2.17, estou usando o git instalado via macports com a variante bash_completion.

Quando eu digito git checkout m<tab>. por exemplo, concluo para master.

No entanto, eu tenho um alias para git checkout, gco. Quando digito gco m<tab>, não obtenho o nome do ramo preenchido automaticamente.

Idealmente, eu gostaria que o preenchimento automático funcionasse magicamente para todos os meus aliases. É possível? Caso contrário, gostaria de personalizá-lo manualmente para cada alias. Então, como eu vou?

kch
fonte
3
complete -o default -o nospace -F não funciona hoje em dia
dezoito anos 22/13
5
Perguntas com mais
votos positivos do
2
Outra resposta do superusuário como alguém apontou para mim que a minha pergunta era um engano dessa. superuser.com/questions/436314/…
dstarh 21/01

Respostas:

183

Conforme declarado nos comentários acima,

complete -o default -o nospace -F _git_checkout gco

não funcionará mais. No entanto, há uma __git_completefunção no git-conclusão.bash que pode ser usada para configurar a conclusão de aliases como:

__git_complete gco _git_checkout
chris_sutter
fonte
6
Esta é a única resposta correta que eu já vi entre muitas erradas.
eighteyes
45
Se você usar o alias global "g" para o git, também poderá adicionar __git_complete g __git_mainpara que a conclusão do código funcione em todos os comandos do git.
Ondrej Machulda
5
^^ Para quem é novo no git / shell / bash. O comentário acima se refere a um alias global do shell, não a um alias nativo do git.
Elias Lynn
14
Onde devo colocar isso?
benregn
15
Finalmente descobri como fazer isso corretamente! Etapa 1) Copie git-completion.basha partir <your git install folder>/etc/bash-completion.d/de ~/.git-completion.bash Passo 2) adicionar source ~/.git-completion.bashà sua .bash_profile Passo 3) Adicionar __git_complete gco _git_checkoutqualquer lugar após a linha acima em seu .bash_profile. Etapa 4) Reinicie o shell e aproveite a conclusão automática do seu alias! :)
kpsfoo 05/04
54

Também encontrei esse problema e criei esse trecho de código. Isso fornecerá automaticamente a conclusão de todos os aliases. Execute-o após declarar todos (ou qualquer) alias.

# wrap_alias takes three arguments:
# $1: The name of the alias
# $2: The command used in the alias
# $3: The arguments in the alias all in one string
# Generate a wrapper completion function (completer) for an alias
# based on the command and the given arguments, if there is a
# completer for the command, and set the wrapper as the completer for
# the alias.
function wrap_alias() {
  [[ "$#" == 3 ]] || return 1

  local alias_name="$1"
  local aliased_command="$2"
  local alias_arguments="$3"
  local num_alias_arguments=$(echo "$alias_arguments" | wc -w)

  # The completion currently being used for the aliased command.
  local completion=$(complete -p $aliased_command 2> /dev/null)

  # Only a completer based on a function can be wrapped so look for -F
  # in the current completion. This check will also catch commands
  # with no completer for which $completion will be empty.
  echo $completion | grep -q -- -F || return 0

  local namespace=alias_completion::

  # Extract the name of the completion function from a string that
  # looks like: something -F function_name something
  # First strip the beginning of the string up to the function name by
  # removing "* -F " from the front.
  local completion_function=${completion##* -F }
  # Then strip " *" from the end, leaving only the function name.
  completion_function=${completion_function%% *}

  # Try to prevent an infinite loop by not wrapping a function
  # generated by this function. This can happen when the user runs
  # this twice for an alias like ls='ls --color=auto' or alias l='ls'
  # and alias ls='l foo'
  [[ "${completion_function#$namespace}" != $completion_function ]] && return 0

  local wrapper_name="${namespace}${alias_name}"

  eval "
function ${wrapper_name}() {
  let COMP_CWORD+=$num_alias_arguments
  args=( \"${alias_arguments}\" )
  COMP_WORDS=( $aliased_command \${args[@]} \${COMP_WORDS[@]:1} )
  $completion_function
  }
"

  # To create the new completion we use the old one with two
  # replacements:
  # 1) Replace the function with the wrapper.
  local new_completion=${completion/-F * /-F $wrapper_name }
  # 2) Replace the command being completed with the alias.
  new_completion="${new_completion% *} $alias_name"

  eval "$new_completion"
}

# For each defined alias, extract the necessary elements and use them
# to call wrap_alias.
eval "$(alias -p | sed -e 's/alias \([^=][^=]*\)='\''\([^ ][^ ]*\) *\(.*\)'\''/wrap_alias \1 \2 '\''\3'\'' /')"

unset wrap_alias
Hesky Fisher
fonte
6
a linha let COMP_CWORD+=$num_alias_argumentsnão funcionou no Mac OS X por algum motivo. Substituí-lo por ((COMP_CWORD+=$num_alias_arguments))corrigido embora
Mario F
5
Uau, isso é incrível - obrigado! wrap_aliasengasga com aspas duplas na definição de alias, e acho que não faz muito sentido aliases de comando múltiplo ( alias 'foo=bar; baz'), então estou colocando um extra | grep -v '[";|&]'após o alias -p. Além disso, fica um pouco lento para centenas de definições de alias, mas fico feliz em confirmar que usar em echovez de evale canalizar a saída em um arquivo de cache (que pode ser evaleditado de uma só vez) funciona bem e é super-rápido .
Jo Liss
2
Outra dica: wrap_aliasrequer que as conclusões sejam configuradas, então tive que me mover source /etc/bash_completionna frente do wrap_aliascódigo.
Jo Liss
2
Isso funcionou para mim no OS X 10.7.2 depois de alterar a linha let COMP_CWORD+=$num_alias_argumentspara let \"COMP_CWORD+=$num_alias_arguments\".
irh 31/10/11
7
Veja a versão atualizada desse script em superuser.com/a/437508/102281 (por exemplo, adicionei suporte para COMP_LINE e COMP_POINT, necessários para algumas finalizações do git).
John Mellor
18

Em git-completion.bashhá uma linha:

complete -o default -o nospace -F _git git

Observando essa linha (e a função _git), você pode adicionar esta linha ao seu .bash_profile:

complete -o default -o nospace -F _git_checkout gco
Chris Lloyd
fonte
4
alguns dos git * funções festança não funcionam mais usando este método
cmcginty
Sim, isso funcionou muito bem até que algo mudou no git_completion.bash ... Agora funciona com o comando completo, mas não com o alias.
Michael Smith
Veja o final desta página para obter respostas que funcionam no git moderno.
eighteyes
Não devemos alterar a resposta aceita para que seja a "correta" ou pelo menos atualizar a resposta aceita para refletir a alteração?
Tony K.
isso funciona bem - adicionou isso ao meu .bash_profile e funciona bem com e sem aliases até o momento: github.com/larrybotha/dotfiles/blob/master/…
Larry
15

Eu tenho o alias g = 'git' e, combinado com os meus aliases do git, digito coisas como

$ g co <branchname>

A correção mais simples para o meu caso de uso específico foi adicionar uma única linha à conclusão do git.

Logo abaixo desta linha:

__git_complete git _git

Eu adicionei esta linha para lidar com meu único apelido 'g':

__git_complete g _git
wonderfulthunk
fonte
2
(Estou usando o Cygwin.) Não consegui encontrar o arquivo git-completionou essa linha /etc/bash_completion.d/git, mas adicionei complete -o default -o nospace -F _git gdepois do meu pseudônimo .bash_aliasese funcionou!
Idbrii
Cuidado, se você editar um arquivo /etc/bash-completion.d/ou entrar novamente /usr/share/bash-completion/, perderá suas alterações sempre que esse arquivo for atualizado usando o gerenciador de pacotes.
Kub1x
14

Idealmente, eu gostaria que o preenchimento automático funcionasse magicamente para todos os meus aliases. É possível?

Sim, é possível com o projeto complete-alias (no Linux). O suporte para Mac é experimental, mas os usuários relataram sucesso.

Cyker
fonte
4
muito obrigado, isso é muito melhor do que descobrir como todos os utilitários do mundo implementam a conclusão do bash.
Art
2
De fato, economizei algum tempo configurando conclusões para aliases.
Samir Alajmovic 5/09/17
2
Funciona como um encanto no Linux (não testado no Mac). Obrigado por escrever!
precisa
1
Isso é demais! Simplesmente funciona, sem preocupações, muito melhor! Obrigado!
emi
5

Você também pode tentar usar aliases do Git. Por exemplo, no meu ~/.gitconfigarquivo, tenho uma seção que se parece com isso:

[alias]
        co = checkout

Então você pode digitar git co m<TAB>, e isso deve ser expandido para git co master, que é o git checkoutcomando.

mipadi
fonte
5

Esta página do fórum mostra uma solução.

Coloque essas linhas em seu .bashrcou .bash_profile:

# Author.: Ole J
# Date...: 23.03.2008
# License: Whatever

# Wraps a completion function
# make-completion-wrapper <actual completion function> <name of new func.>
#                         <command name> <list supplied arguments>
# eg.
#   alias agi='apt-get install'
#   make-completion-wrapper _apt_get _apt_get_install apt-get install
# defines a function called _apt_get_install (that's $2) that will complete
# the 'agi' alias. (complete -F _apt_get_install agi)
#
function make-completion-wrapper () {
    local function_name="$2"
    local arg_count=$(($#-3))
    local comp_function_name="$1"
    shift 2
    local function="
function $function_name {
    ((COMP_CWORD+=$arg_count))
    COMP_WORDS=( "$@" \${COMP_WORDS[@]:1} )
    "$comp_function_name"
    return 0
}"
    eval "$function"
}

# and now the commands that are specific to this SO question

alias gco='git checkout'

# we create a _git_checkout_mine function that will do the completion for "gco"
# using the completion function "_git"
make-completion-wrapper _git _git_checkout_mine git checkout

# we tell bash to actually use _git_checkout_mine to complete "gco"
complete -o bashdefault -o default -o nospace -F _git_checkout_mine gco

Esta solução é semelhante ao script do balshetzer , mas somente este realmente funciona para mim. (o script do balshetzer teve problemas com alguns dos meus pseudônimos.)

hcs42
fonte
; Isso quase funciona - eu recebo alguns erros, mas a conclusão continua. Mais alguma coisa que eu possa fazer? -bash: eval: line 28: unexpected EOF while looking for matching ''' -bash: eval: line 29: syntax error: unexpected end of file
pforhan
@pforhan Eu posso ver citando problemas acima ... as "aspas dentro da functionstring devem ser citadas como \". Provavelmente, você deve comer uma de suas 'citações em algum lugar ao longo da linha.
Tom Hale
5

Mais uma opção é usar o ~/.bash_completionarquivo. Para criar o gcoalias para git checkoutapenas colocar isso lá:

_xfunc git __git_complete gco _git_checkout

Então, ~/.bashrcvocê deve colocar apenas o apelido em si:

alias gco='git checkout'

Duas linhas. É isso aí.

Explicação:

O ~/bash_completioncódigo é obtido no final do script bash_completion principal. No gentoo, encontrei o script principal em/usr/share/bash-completion/bash_completion .

O _xfunc gitbit cuida da fonte do git-completionarquivo para você, para que você não precise colocar mais nada ~/.bashrc.

A resposta aceita exige que você copie .git-completion.she ~/.bashrcobtenha o código do seu arquivo, o que eu acho ruim.


PS: Ainda estou tentando descobrir como não originar o git-completionscript inteiro no meu ambiente bash. Comente ou edite se encontrar uma maneira.

kub1x
fonte
Por que é _xfunc gitnecessário?
Tom Hale
@ TomHale Tentei melhorar a resposta. Em vez de fazer source ~/.git-completion.sh, deixo _xfuncfazer isso por mim. Ele só se sente mais bonitos e de fazê-lo apenas em ~/.bash_completion. Sem o _xfunc(ou o fornecimento) a __git_completefunção não existe.
Kub1x
1
Não há necessidade do ~/.bash_completionarquivo - a _xfunclinha funciona para mim .bashrc.
Tom Hale
2

Você apenas precisa encontrar o completecomando e duplicar a linha com o nome alternativo.

Eu tenho alias d-m="docker-machine". Em palavras, d-mdeve ser o apelido para docker-machine.

Assim, no Mac (via brew), os arquivos de conclusão estão cd `brew --prefix`/etc/bash_completion.d/.
Para o meu caso, editei o arquivo chamado docker-machine.
Todo o caminho na parte inferior havia:

complete -F _docker_machine docker-machine

Acabei de adicionar outra linha, com meu apelido:

complete -F _docker_machine docker-machine
complete -F _docker_machine d-m
luckydonald
fonte
Esta é a melhor solução para aliases simples (um a um), como dockeralias d. Embora, para o exemplo da pergunta, o git checkoutalias para gcoseja mais complexo.
wisbucky
1

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):

# 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

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

A _completion_loaderlinha pode não ser necessária. Mas, em algumas situações, a função de conclusão é carregada dinamicamente somente depois que você digita o comando e pressiona TABna primeira vez. Portanto, se você não tiver usado o comando original e tentar o alias + TAB, poderá receber um erro como "bash: conclusão: função '_docker' não encontrada".

wisbucky
fonte
1

Há muitas respostas para essa pergunta e, como eu, aposto que muitos leitores confusos. Para o meu caso, eu também tinha o requisito de que meus dotfiles funcionassem em várias plataformas com diferentes versões do Git. Eu também não, alias g=gitmas tenhog defini como uma função.

Para conseguir isso, tive que reunir respostas diferentes aqui em uma solução. Embora isso reitere as respostas, eu já pensei que alguém no meu barco poderia achar essa compilação útil como eu teria quando cheguei a essa pergunta.

Isso pressupõe a conclusão mais antiga e mais recente do Git, os padrões do Ubuntu e brew install git no MacOS. No caso posterior, as conclusões concluídas instaladas pelo brew não estavam sendo processadas pelo bash (algo que vou diagnosticar mais tarde).

# Alias g to git

g() {
  if [[ $# > 0 ]]; then
    git "$@"
  else
    git status -sb
  fi
}

# Preload git completion in Ubuntu which is normally lazy loaded but we need
# the __git_wrap__git_main function available for our completion.
if [[ -e /usr/share/bash-completion/completions/git ]]; then
  source /usr/share/bash-completion/completions/git
elif [[ -e /usr/local/etc/bash_completion.d/git-completion.bash ]]; then
  source /usr/local/etc/bash_completion.d/git-completion.bash
fi

if command_exists __git_complete; then
  __git_complete g _git
elif command_exists __git_wrap__git_main; then
  complete -o bashdefault -o default -o nospace -F __git_wrap__git_main g
fi
Sukima
fonte
0

Se você usar alias g='git', eu adiciono esta linha de código em.bash_aliases

complete -o default -o nospace -F _git g
Druta Ruslan
fonte