¿Cómo consigo completar bash para trabajar con alias?

195

Caso en punto:

Estoy en Mac con bash v3.2.17, estoy usando git instalado a través de macports con la variante bash_completion.

Cuando escribo git checkout m<tab>. por ejemplo, lo completo a master.

Sin embargo, tengo un alias git checkout, gco. Cuando escribo gco m<tab>, no se completa automáticamente el nombre de la sucursal.

Idealmente, me gustaría que el autocompletado funcione mágicamente para todos mis alias. ¿Es posible? De lo contrario, me gustaría personalizarlo manualmente para cada alias. Entonces, ¿cómo hago?

kch
fuente
3
completo -o predeterminado -o nospace -F no funciona hoy en día
eightteyes
55
Las preguntas con más votos a favor que la respuesta principal a menudo implican grandes solicitudes de funciones
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
2
Otra respuesta del superusuario cuando alguien me señaló que mi pregunta era engañada. superuser.com/questions/436314/...
dstarh

Respuestas:

183

Como se indicó en los comentarios anteriores,

complete -o default -o nospace -F _git_checkout gco

Ya no funcionará. Sin embargo, hay una __git_completefunción en git-completar.bash que se puede usar para configurar la finalización de alias de esta manera:

__git_complete gco _git_checkout
chris_sutter
fuente
66
Esta es la única respuesta correcta que he visto entre muchas equivocadas.
eighteyes
45
Si usa el alias global "g" para git, también puede agregar __git_complete g __git_mainpara que el código complete en todos los comandos de git.
Ondrej Machulda
55
^^ Para aquellos nuevos en git / shell / bash. El comentario anterior se refiere a un alias de shell global, no a un alias git nativo.
Elijah Lynn
14
¿Dónde debería poner esto?
benregn
15
Finalmente descubrí cómo hacer esto correctamente. Paso 1) Copia git-completion.bashde <your git install folder>/etc/bash-completion.d/al ~/.git-completion.bash paso 2) añadir source ~/.git-completion.basha su .bash_profile paso 3) Añadir __git_complete gco _git_checkouten cualquier lugar después de la línea anterior en su .bash_profile. Paso 4) ¡Reinicia el shell y disfruta de tu autocompletado de alias! :)
kpsfoo
54

Me encontré con este problema también y se me ocurrió este fragmento de código. Esto le dará automáticamente la finalización de todos los alias. Ejecútelo después de declarar todo (o cualquier) 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
fuente
66
la línea let COMP_CWORD+=$num_alias_argumentsno funcionó en Mac OS X por alguna razón. Sin embargo, reemplazarlo con ((COMP_CWORD+=$num_alias_arguments))arreglado
Mario F
55
Wow, eso es increíble, ¡gracias! wrap_aliasse ahoga con comillas dobles en la definición de alias, y supongo que no tiene mucho sentido para los alias de comandos múltiples ( alias 'foo=bar; baz'), así que pongo un extra | grep -v '[";|&]'después de alias -p. Además, se vuelve un poco lento para cientos de definiciones de alias, pero estoy feliz de confirmar que usar en echolugar de evaly canalizar la salida en un archivo de caché (que luego se puede evaleditar de una vez) funciona bien y es súper rápido .
Jo Liss
2
Otra sugerencia: wrap_aliasrequiere que se completen las terminaciones, por lo que tuve que moverme source /etc/bash_completiondelante del wrap_aliascódigo.
Jo Liss
2
Esto funcionó para mí en OS X 10.7.2 después de cambiar la línea let COMP_CWORD+=$num_alias_argumentsa let \"COMP_CWORD+=$num_alias_arguments\".
irh
77
Consulte la versión actualizada de este script en superuser.com/a/437508/102281 (por ejemplo, agregué soporte para COMP_LINE y COMP_POINT que son necesarios para algunas terminaciones de git).
John Mellor
18

En git-completion.bashhay una línea:

complete -o default -o nospace -F _git git

Mirando esa línea (y la función _git) puede agregar esta línea a su .bash_profile:

complete -o default -o nospace -F _git_checkout gco
Chris Lloyd
fuente
44
algunas de las funciones de git * bash ya no funcionan con este método
cmcginty
Sí, esto solía funcionar muy bien hasta que algo cambió en git_completion.bash ... Ahora funciona con el comando completo pero no con el alias.
Michael Smith
Consulte el final de esta página para obtener respuestas que funcionan en git moderno.
eighteyes
¿No deberíamos cambiar la respuesta aceptada para que sea la "correcta", o al menos actualizar la respuesta aceptada para reflejar el cambio?
Tony K.
esto funciona bien - agregué esto a mi .bash_profile, y funciona bien con y sin alias hasta ahora: github.com/larrybotha/dotfiles/blob/master/…
Larry
15

He alias g = 'git', y combinado con mis alias git escribo cosas como

$ g co <branchname>

La solución más simple para mi caso de uso específico fue agregar una sola línea para completar git.

Justo debajo de esta línea:

__git_complete git _git

Agregué esta línea para manejar mi solo alias 'g':

__git_complete g _git
maravilloso pensamiento
fuente
2
(Estoy usando Cygwin.) No pude encontrar el archivo git-completiono esa línea /etc/bash_completion.d/git, pero agregué complete -o default -o nospace -F _git gdespués de mi alias .bash_aliasesy funcionó.
idbrii
Tenga en cuenta que si edita un archivo en /etc/bash-completion.d/o recientemente /usr/share/bash-completion/, perderá sus cambios cada vez que ese archivo se actualice utilizando su administrador de paquetes.
kub1x
14

Idealmente, me gustaría que el autocompletado funcione mágicamente para todos mis alias. ¿Es posible?

Sí, es posible con el proyecto de alias completo (en Linux). El soporte para Mac es experimental, pero los usuarios han informado de éxito.

Cyker
fuente
44
muchas gracias, esto es mucho mejor que descubrir cómo cada utilidad en el mundo implementa la finalización de bash.
artículo
2
De hecho, me ahorró algo de tiempo configurando terminaciones para alias.
Samir Alajmovic
2
Funciona de maravilla en Linux (no probado en Mac). Gracias por escribirlo!
bitmask
1
¡Esto es simplemente increíble! Simplemente funciona, no te preocupes, ¡mucho mejor! ¡Gracias!
emi
5

También podría intentar usar alias Git. Por ejemplo, en mi ~/.gitconfigarchivo, tengo una sección que se ve así:

[alias]
        co = checkout

Entonces podría escribir git co m<TAB>, y eso debería expandirse a git co master, que es el git checkoutcomando.

mipadi
fuente
5

Esta página del foro muestra una solución.

Pon estas líneas en tu .bashrco .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 solución es similar al script de balshetzer , pero solo esta realmente funciona para mí. (El guión de balshetzer tuvo problemas con algunos de mis alias).

hcs42
fuente
; Esto casi funciona: me sale un par de errores, pero la finalización se completa. ¿Algo más que pueda hacer? -bash: eval: line 28: unexpected EOF while looking for matching ''' -bash: eval: line 29: syntax error: unexpected end of file
pforhan
@pforhan Puedo ver problemas de "citas arriba ... las comillas dentro de la functioncadena deben ser citadas como \". Esto probablemente se come una de sus 'citas en algún lugar a lo largo de la línea.
Tom Hale
5

Una opción más es usar el ~/.bash_completionarchivo. Para crear el gcoalias para git checkoutsimplemente poner esto allí:

_xfunc git __git_complete gco _git_checkout

Luego ~/.bashrc, debe poner solo el alias:

alias gco='git checkout'

Dos lineas. Eso es.

Explicación:

Se ~/bash_completionobtiene al final del script principal bash_completion. En gentoo encontré el guión principal en/usr/share/bash-completion/bash_completion .

El _xfunc gitbit se encarga de obtener el git-completionarchivo por usted para que no necesite agregar nada más ~/.bashrc.

La respuesta aceptada requiere que la copie .git-completion.shy la obtenga de su ~/.bashrcarchivo, lo que me parece poco convincente.


PD: Todavía estoy tratando de descubrir cómo no generar el git-completionscript completo en mi entorno bash. Comenta o edita si encuentras la manera.

kub1x
fuente
¿Por qué se _xfunc gitrequiere?
Tom Hale
@TomHale Traté de mejorar la respuesta. En lugar de hacerlo source ~/.git-completion.sh, dejo _xfunchacerlo por mí. Simplemente se siente mejor y más limpio hacerlo solo en ~/.bash_completion. Sin el _xfunc(o el abastecimiento) la __git_completefunción no existe.
kub1x
1
No es necesario el ~/.bash_completionarchivo, la _xfunclínea me funciona .bashrc.
Tom Hale
2

Solo tiene que encontrar el completecomando y duplicar la línea que tiene el nombre de alias.

Tengo alias d-m="docker-machine". En palabras, d-mserá el alias para docker-machine.

Entonces, en Mac (a través de brew), los archivos de finalización están en cd `brew --prefix`/etc/bash_completion.d/.
Para mi caso, edité el archivo llamado docker-machine.
Todo el camino al fondo había:

complete -F _docker_machine docker-machine

Así que acabo de agregar otra línea, con mi alias:

complete -F _docker_machine docker-machine
complete -F _docker_machine d-m
luckydonald
fuente
Esta es la mejor solución para alias simples (uno a uno), como dockeralias d. Aunque para el ejemplo en la pregunta, git checkoutalias a gcoes más complejo.
wisbucky
1

Primero, busque el comando de finalización original. Ejemplo:

$ complete | grep git

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

Ahora agréguelos a su script de inicio (por ejemplo, ~ / .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

La _completion_loaderlínea puede no ser requerida. Pero para algunas situaciones, la función de finalización solo se carga dinámicamente después de escribir el comando y presionar TABla primera vez. Por lo tanto, si no ha utilizado el comando original y prueba el alias + TAB, puede obtener un error como "bash: complete: function '_docker' not found".

Wisbucky
fuente
1

Hay muchas respuestas a esta pregunta y, como yo, apuesto a muchos lectores confundidos. Para mi caso, también tuve el requisito de que mis archivos dot funcionaran en múltiples plataformas con diferentes versiones de Git. Tampoco lo hago, alias g=gitsino que lo he gdefinido como una función

Para lograr esto, tuve que juntar diferentes respuestas aquí en una sola solución. Aunque esto ya reitera las respuestas, pensé que alguien en mi bote podría encontrar esta compilación útil como lo habría hecho cuando llegué a esta pregunta.

Esto supone la finalización de Git más antigua y más reciente, los valores predeterminados de Ubuntu y brew install giten MacOS. En el último caso, las terminaciones instaladas en brew no estaban siendo procesadas por bash (algo que diagnosticaré más adelante).

# 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
fuente
0

Si usa alias g='git', agrego esta línea de código en.bash_aliases

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