Comment puis-je obtenir l'achèvement de bash pour travailler avec des alias?

195

Exemple concret:

Je suis sur mac avec bash v3.2.17, j'utilise git installé via macports avec la variante bash_completion.

Quand je tape git checkout m<tab>. par exemple, je le termine master.

Cependant, j'ai un alias git checkout, gco. Lorsque je tape gco m<tab>, je ne reçois pas le nom de la branche renseigné automatiquement.

Idéalement, j'aimerais que la saisie semi-automatique fonctionne comme par magie pour tous mes alias. C'est possible? A défaut, je souhaite le personnaliser manuellement pour chaque alias. Alors, comment dois-je procéder non plus?

kch
la source
3
complete -o default -o nospace -F ne fonctionne pas de nos jours
eighteyes
5
Les questions avec plus de votes positifs que la réponse du haut impliquent souvent de grandes demandes de fonctionnalités
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
2
Une autre réponse du superutilisateur car quelqu'un m'a fait remarquer que ma question était dupe de celle-ci. superuser.com/questions/436314/…
dstarh

Réponses:

183

Comme indiqué dans les commentaires ci-dessus,

complete -o default -o nospace -F _git_checkout gco

ne fonctionnera plus. Cependant, il y a une __git_completefonction dans git-complétement.bash qui peut être utilisée pour configurer la complétion pour des alias comme ceci:

__git_complete gco _git_checkout
chris_sutter
la source
6
C'est la seule bonne réponse que j'ai vue parmi de nombreuses mauvaises.
eighteyes
45
Si vous utilisez l'alias global "g" pour git, vous pouvez également l'ajouter __git_complete g __git_mainpour que la complétion de code fonctionne sur toutes les commandes git.
Ondrej Machulda
5
^^ Pour les nouveaux utilisateurs de git / shell / bash. Le commentaire ci-dessus fait référence à un alias shell global, et non à un alias git natif.
Elijah Lynn
14
Où dois-je mettre ça?
benregn
15
Enfin compris comment le faire correctement! Étape 1) Copiez git-completion.bashde <your git install folder>/etc/bash-completion.d/à l' ~/.git-completion.bash étape 2) ajoutez source ~/.git-completion.bashà votre .bash_profile étape 3) Ajoutez __git_complete gco _git_checkoutn'importe où après la ligne ci-dessus dans votre .bash_profile. Étape 4) Redémarrez le shell et profitez de la saisie automatique de votre alias! :)
kpsfoo
54

J'ai également rencontré ce problème et j'ai trouvé cet extrait de code. Cela vous donnera automatiquement l'achèvement de tous les alias. Exécutez-le après avoir déclaré tout (ou n'importe quel) 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
la source
6
la ligne let COMP_CWORD+=$num_alias_argumentsne fonctionnait pas sur Mac OS X pour une raison quelconque. Le remplacer par le ((COMP_CWORD+=$num_alias_arguments))corriger cependant
Mario F
5
Wow, c'est génial - merci! wrap_aliasétouffe les guillemets doubles dans la définition d'alias, et je suppose que cela n'a pas beaucoup de sens pour les alias multi-commandes ( alias 'foo=bar; baz'), donc je mets un supplément | grep -v '[";|&]'après le alias -p. En outre, cela devient un peu lent pour des centaines de définitions d'alias, mais je suis heureux de confirmer que l'utilisation echoau lieu de evalet la canalisation de la sortie dans un fichier cache (qui peut ensuite être evalédité en une seule fois) fonctionne bien et est super rapide .
Jo Liss
2
Autre indice: wrap_aliasnécessite que les compléments soient configurés, j'ai donc dû me déplacer source /etc/bash_completiondevant le wrap_aliascode.
Jo Liss
2
Cela a fonctionné pour moi sur OS X 10.7.2 après avoir changé la ligne let COMP_CWORD+=$num_alias_argumentsen let \"COMP_CWORD+=$num_alias_arguments\".
IRH
7
Voir la version mise à jour de ce script sur superuser.com/a/437508/102281 (par exemple, j'ai ajouté la prise en charge de COMP_LINE et COMP_POINT qui sont nécessaires pour certaines finitions de git).
John Mellor
18

Il git-completion.bashy a une ligne:

complete -o default -o nospace -F _git git

En regardant cette ligne (et la fonction _git), vous pouvez ajouter cette ligne à votre .bash_profile:

complete -o default -o nospace -F _git_checkout gco
Chris Lloyd
la source
4
certaines des fonctions git * bash ne fonctionnent plus en utilisant cette méthode
cmcginty
Oui, cela fonctionnait très bien jusqu'à ce que quelque chose change dans git_completion.bash ... Maintenant, cela fonctionne avec la commande complète mais pas avec l'alias.
Michael Smith
Voir la fin de cette page pour les réponses qui fonctionnent dans git moderne.
eighteyes
Ne devrions-nous pas changer la réponse acceptée pour qu'elle soit la "bonne", ou au moins mettre à jour la réponse acceptée pour refléter le changement?
Tony K.
cela fonctionne bien - l'ajoute à mon .bash_profile, et fonctionne très bien avec et sans alias jusqu'à présent: github.com/larrybotha/dotfiles/blob/master/…
Larry
15

J'ai alias g = 'git', et combiné avec mes alias git, je tape des choses comme

$ g co <branchname>

La solution la plus simple pour mon cas d'utilisation spécifique était d'ajouter une seule ligne à git-complétement.

Juste en dessous de cette ligne:

__git_complete git _git

J'ai ajouté cette ligne pour gérer mon seul alias «g»:

__git_complete g _git
amazingthunk
la source
2
(J'utilise Cygwin.) Je n'ai pas pu trouver le fichier git-completionou cette ligne /etc/bash_completion.d/git, mais j'ai ajouté complete -o default -o nospace -F _git gaprès mon alias .bash_aliaseset cela a fonctionné!
idbrii
Attention, si vous modifiez un fichier dans /etc/bash-completion.d/ou récemment /usr/share/bash-completion/, vous perdrez vos modifications chaque fois que ce fichier sera mis à jour à l'aide de votre gestionnaire de packages.
kub1x
14

Idéalement, j'aimerais que la saisie semi-automatique fonctionne comme par magie pour tous mes alias. C'est possible?

Oui, c'est possible avec le projet complete-alias (sous Linux). La prise en charge de Mac est expérimentale, mais les utilisateurs ont signalé un succès.

Cyker
la source
4
merci beaucoup, c'est tellement mieux que de comprendre comment chaque utilitaire dans le monde implémente l'achèvement de bash.
artm
2
En effet, cela m'a fait gagner du temps lors de la configuration des complétions pour les alias.
Samir Alajmovic
2
Fonctionne comme un charme sous Linux (non testé sur Mac). Merci de l'avoir écrit!
bitmask
1
C'est tout simplement génial! Cela fonctionne, sans chichis, bien mieux! Merci!
emi
5

Vous pouvez également essayer d'utiliser des alias Git. Par exemple, dans mon ~/.gitconfigfichier, j'ai une section qui ressemble à ceci:

[alias]
        co = checkout

Vous pouvez donc taper git co m<TAB>, et cela devrait s'étendre à git co master, qui est la git checkoutcommande.

mipadi
la source
5

Cette page de forum montre une solution.

Mettez ces lignes dans votre .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

Cette solution est similaire au script de Balshetzer , mais seule celle-ci fonctionne réellement pour moi. (Le script de balshetzer avait des problèmes avec certains de mes alias.)

hcs42
la source
; Cela fonctionne presque - je reçois quelques erreurs, mais l'achèvement est terminé. Autre chose que je peux faire? -bash: eval: line 28: unexpected EOF while looking for matching ''' -bash: eval: line 29: syntax error: unexpected end of file
pforhan
@pforhan Je peux voir les problèmes de citation ci-dessus ... les "guillemets à l'intérieur de la functionchaîne doivent être cités comme \". Cela mange probablement l'une de vos 'citations quelque part le long de la ligne.
Tom Hale
5

Une autre option consiste à utiliser le ~/.bash_completionfichier. Pour créer l' gcoalias pour git checkoutsimplement mettre ceci ici:

_xfunc git __git_complete gco _git_checkout

Ensuite, ~/.bashrcvous devez simplement mettre l'alias lui-même:

alias gco='git checkout'

Deux lignes. C'est tout.

Explication:

Le ~/bash_completionobtient à la fin du script bash_completion principal. Dans gentoo, j'ai trouvé le script principal dans/usr/share/bash-completion/bash_completion .

Le _xfunc gitbit s'occupe de trouver le git-completionfichier pour vous, vous n'avez donc pas besoin de mettre quoi que ce soit d'autre ~/.bashrc.

La réponse acceptée vous oblige à la copier .git-completion.shet à la source à partir de votre ~/.bashrcfichier que je trouve boiteux.


PS: J'essaie toujours de comprendre comment ne pas source tout le git-completionscript dans mon environnement bash. Veuillez commenter ou modifier si vous trouvez un moyen.

kub1x
la source
Pourquoi est _xfunc gitrequis?
Tom Hale
@TomHale J'ai essayé d'améliorer la réponse. Plutôt que de le faire, source ~/.git-completion.shje le laisse _xfuncfaire pour moi. C'est plus agréable et plus propre de le faire uniquement en ~/.bash_completion. Sans _xfunc(ou le sourcing), la __git_completefonction n'existe pas.
kub1x
1
Pas besoin du ~/.bash_completionfichier - la _xfuncligne fonctionne pour moi .bashrc.
Tom Hale
2

Il suffit de trouver le complete commande et de dupliquer la ligne portant le nom d'alias à la place.

Je l'ai alias d-m="docker-machine". En d'autres termes, d-msera l'alias dedocker-machine .

Donc, sur Mac (via brew), les fichiers d'achèvement sont dans cd `brew --prefix`/etc/bash_completion.d/.
Pour mon cas, j'ai édité le fichier appelé docker-machine.
Tout en bas, il y avait:

complete -F _docker_machine docker-machine

Je viens donc d'ajouter une autre ligne, avec mon alias:

complete -F _docker_machine docker-machine
complete -F _docker_machine d-m
luckydonald
la source
Il s'agit de la meilleure solution pour les alias simples (un à un), comme les dockeralias de d. Bien que pour l'exemple de la question, le git checkoutrepliement vers gcosoit plus complexe.
wisbucky
1

Tout d'abord, recherchez la commande d'achèvement d'origine. Exemple:

$ complete | grep git

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

Ajoutez-les maintenant à votre script de démarrage (par exemple ~ / .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_loaderligne peut ne pas être requise. Mais dans certaines situations, la fonction d'achèvement n'est chargée dynamiquement qu'après avoir tapé la commande et appuyé TABla première fois. Donc, si vous n'avez pas utilisé la commande d'origine et essayez l'alias + TAB, vous pouvez obtenir une erreur comme "bash: complétion: fonction '_docker' introuvable".

wisbucky
la source
1

Il y a beaucoup de réponses à cette question et comme moi je parie beaucoup de lecteurs confus. Pour mon cas, j'avais également eu besoin de faire fonctionner mes fichiers dot sur plusieurs plateformes avec différentes versions de Git. Je ne le fais pas non plus alias g=gitmaisg défini comme une fonction.

Pour ce faire, j'ai dû regrouper différentes réponses ici en une seule solution. Bien que cela répète déjà les réponses, je pensais que quelqu'un dans mon bateau pourrait trouver cette compilation utile comme je l'aurais fait lorsque je suis arrivé à cette question.

Cela suppose l'achèvement de Git plus ancien et plus récent, les valeurs par défaut d'Ubuntu et brew install gitsur MacOS. Dans le dernier cas, les achèvements installés de l'infusion n'étaient pas traités par bash (quelque chose que je diagnostiquerai plus tard).

# 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
la source
0

Si vous utilisez alias g='git', j'ajoute cette ligne de code dans.bash_aliases

complete -o default -o nospace -F _git g
Druta Ruslan
la source