Come posso ottenere il completamento bash per lavorare con gli alias?


195

Caso in questione:

Sono un mac con bash v3.2.17, sto usando git installato tramite macports con la variante bash_completion.

Quando scrivo git checkout m<tab>. per esempio, lo ottengo completato master.

Tuttavia, ho un alias git checkout, gco. Quando scrivo gco m<tab>, non ottengo il nome del ramo completato automaticamente.

Idealmente, mi piacerebbe che il completamento automatico funzionasse magicamente per tutti i miei alias. È possibile? In caso contrario, vorrei personalizzarlo manualmente per ogni alias. Quindi, come posso fare?


3
completo -o predefinito -o nospace -F non funziona al giorno d'oggi
ottanta

5
Le domande con più voti rispetto alla risposta più alta spesso implicano grandi richieste di funzionalità
Ciro Santilli 20 冠状 病 六四 事件 法轮功

2
Un'altra risposta del superutente quando qualcuno mi ha fatto notare che la mia domanda era una fregatura di questa. superuser.com/questions/436314/…
dstarh

Risposte:


183

Come indicato nei commenti sopra,

complete -o default -o nospace -F _git_checkout gco

non funzionerà più. Tuttavia, c'è una __git_completefunzione in git-completamento.bash che può essere usata per impostare il completamento per alias in questo modo:

__git_complete gco _git_checkout

6
Questa è l'unica risposta corretta che ho visto tra molte di quelle sbagliate.
ottanta,

45
Se usi alias globale "g" per git, puoi anche aggiungere __git_complete g __git_mainper far funzionare il completamento del codice su tutti i comandi git.
Ondrej Machulda,

5
^^ Per i nuovi utenti di git / shell / bash. Il commento sopra si riferisce a un alias di shell globale, non a un alias git nativo.
Elia Lynn,

14
Dove dovrei metterlo?
benregn,

15
Finalmente ho capito come farlo correttamente! Passaggio 1) Copia git-completion.bashda <your git install folder>/etc/bash-completion.d/al ~/.git-completion.bash passaggio 2) Aggiungi source ~/.git-completion.bashal .bash_profile passaggio 3) Aggiungi __git_complete gco _git_checkoutovunque dopo la riga sopra nel tuo .bash_profile. Step 4) Riavvia la shell e goditi il ​​tuo completamento automatico alias! :)
kpsfoo,

54

Ho riscontrato anche questo problema e ho trovato questo frammento di codice. Questo ti darà automaticamente il completamento per tutti gli alias. Eseguilo dopo aver dichiarato tutto (o qualsiasi) 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

6
la linea let COMP_CWORD+=$num_alias_argumentsnon ha funzionato su Mac OS X per qualche motivo. Sostituendolo con ((COMP_CWORD+=$num_alias_arguments))risolto però
Mario F

5
Wow, è fantastico - grazie! wrap_aliassoffoca sulle doppie virgolette nella definizione di alias, e immagino che non abbia molto senso per gli alias multi-comando ( alias 'foo=bar; baz'), quindi sto aggiungendo un extra | grep -v '[";|&]'dopo il alias -p. Inoltre, diventa un po 'lento per centinaia di definizioni di alias, ma sono felice di confermare che l'utilizzo echoanziché evale il piping dell'output in un file di cache (che può quindi essere eval' editato in una volta sola) funziona bene ed è super veloce .
Jo Liss,

2
Un altro suggerimento: wrap_aliasrichiede che i completamenti siano impostati, quindi ho dovuto spostarmi source /etc/bash_completiondi fronte al wrap_aliascodice.
Jo Liss,

2
Questo ha funzionato per me su OS X 10.7.2 dopo aver cambiato la linea let COMP_CWORD+=$num_alias_argumentsin let \"COMP_CWORD+=$num_alias_arguments\".
irh

7
Vedi la versione aggiornata di questo script su superuser.com/a/437508/102281 (ad esempio, ho aggiunto il supporto per COMP_LINE e COMP_POINT che sono necessari per alcuni completamenti git).
John Mellor,

18

In git-completion.bashc'è una linea:

complete -o default -o nospace -F _git git

Guardando quella linea (e la funzione _git) puoi aggiungere questa linea al tuo .bash_profile:

complete -o default -o nospace -F _git_checkout gco

4
alcune delle funzioni git * bash non funzionano più con questo metodo
cmcginty,

Sì, questo funzionava alla grande fino a quando qualcosa non è cambiato in git_completion.bash ... Ora funziona con il comando completo ma non con l'alias.
Michael Smith,

Vedi la fine di questa pagina per le risposte che funzionano in git moderno.
ottanta,

Non dovremmo cambiare la risposta accettata in modo che sia "corretta", o almeno aggiornare la risposta accettata per riflettere la modifica?
Tony K.

funziona bene - aggiunto questo al mio .bash_profile, e funziona bene con e senza alias finora: github.com/larrybotha/dotfiles/blob/master/…
Larry

15

Ho alias g = 'git', e in combinazione con i miei alias git scrivo cose come

$ g co <branchname>

La soluzione più semplice per il mio caso d'uso specifico era quella di aggiungere una sola riga al completamento di git.

Proprio sotto questa linea:

__git_complete git _git

Ho aggiunto questa riga per gestire il mio singolo alias 'g':

__git_complete g _git

2
(Sto usando Cygwin.) Non sono riuscito a trovare il file git-completiono quella riga /etc/bash_completion.d/git, ma ho aggiunto il complete -o default -o nospace -F _git gmio alias .bash_aliasese ha funzionato!
idbrii,

Attenzione, se si modifica un file in /etc/bash-completion.d/o appena dentro /usr/share/bash-completion/, si perderanno le modifiche ogni volta che quel file viene aggiornato utilizzando il proprio gestore pacchetti.
kub1x,

14

Idealmente, mi piacerebbe che il completamento automatico funzionasse magicamente per tutti i miei alias. È possibile?

Sì, è possibile con il progetto alias completo (su Linux). Il supporto per Mac è sperimentale ma gli utenti hanno segnalato il successo.


4
grazie mille, è molto meglio che capire come ogni utility al mondo implementa il completamento bash.
artm

2
In effetti, mi ha risparmiato un po 'di tempo a configurare i completamenti per gli alias.
Samir Alajmovic,

2
Funziona come un fascino su Linux (non testato su Mac). Grazie per averlo scritto!
Maschera di bit

1
Questo è semplicemente fantastico! Funziona solo, senza problemi, molto meglio! Grazie!
emi

5

Puoi anche provare a usare gli alias Git. Ad esempio, nel mio ~/.gitconfigfile, ho una sezione che assomiglia a questa:

[alias]
        co = checkout

Quindi potresti digitare git co m<TAB>, e quello dovrebbe espandersi a git co master, che è il git checkoutcomando.


5

Questa pagina del forum mostra una soluzione.

Inserisci queste righe nel tuo .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

Questa soluzione è simile alla sceneggiatura di balshetzer , ma solo questa funziona davvero per me. (la sceneggiatura di balshetzer ha avuto problemi con alcuni dei miei alias.)


; Questo funziona quasi - ottengo un paio di errori, ma il completamento va a buon fine. Qualcos'altro che posso fare? -bash: eval: line 28: unexpected EOF while looking for matching ''' -bash: eval: line 29: syntax error: unexpected end of file
pforhan,

@pforhan Riesco a vedere citando i problemi sopra ... le "virgolette all'interno della functionstringa dovrebbero essere citate come \". Questo probabilmente mangia una delle tue 'citazioni da qualche parte lungo la linea.
Tom Hale,

5

Un'altra opzione è utilizzare il ~/.bash_completionfile. Per creare l' gcoalias per git checkoutinserire semplicemente questo:

_xfunc git __git_complete gco _git_checkout

Quindi ~/.bashrcdevi inserire solo lo pseudonimo:

alias gco='git checkout'

Due linee. Questo è tutto.

Spiegazione:

Il ~/bash_completionottiene di provenienza alla fine dello script bash_completion principale. In gentoo ho trovato lo script principale in /usr/share/bash-completion/bash_completion.

Il _xfunc gitbit si occupa di reperire il git-completionfile per te, quindi non è necessario inserire altro ~/.bashrc.

La risposta accettata richiede di copiarla .git-completion.she reperirla dal tuo ~/.bashrcfile che trovo zoppo.


PS: Sto ancora cercando di capire come non generare l'intero git-completionscript nel mio ambiente bash. Commenta o modifica se trovi un modo.


Perché è _xfunc gitrichiesto?
Tom Hale,

@TomHale Ho provato a migliorare la risposta. Invece di farlo, source ~/.git-completion.shlo lascio _xfuncfare per me. È più bello e più pulito farlo solo in ~/.bash_completion. Senza il _xfunc(o il sourcing) la __git_completefunzione non esiste.
kub1x,

1
Non c'è bisogno del ~/.bash_completionfile: la _xfunclinea funziona per me .bashrc.
Tom Hale,

2

Devi solo trovare il completecomando e duplicare la riga con il nome alias invece.

Io ho alias d-m="docker-machine". A parole, d-msarà lo pseudonimo di docker-machine.

Quindi su Mac (via brew), i file di completamento sono presenti cd `brew --prefix`/etc/bash_completion.d/.
Per il mio caso ho modificato il file chiamato docker-machine.
In fondo c'era:

complete -F _docker_machine docker-machine

Quindi ho appena aggiunto un'altra riga, con il mio alias:

complete -F _docker_machine docker-machine
complete -F _docker_machine d-m

Questa è la soluzione migliore per alias semplici (da uno a uno), come quelli con dockeralias d. Sebbene per l'esempio nella domanda, git checkoutaliasing gcoè più complesso.
Wisbucky

1

Innanzitutto, cerca il comando di completamento originale. Esempio:

$ complete | grep git

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

Ora aggiungili al tuo script di avvio (ad es. ~ / .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

Il _completion_loader linea potrebbe non essere richiesta. Ma per alcune situazioni, la funzione di completamento viene caricata in modo dinamico solo dopo aver digitato il comando e premuto TABla prima volta. Quindi se non hai usato il comando originale e provi l'alias + TAB, potresti ricevere un errore come "bash: completamento: funzione '_docker' non trovata".


1

Ci sono molte risposte a questa domanda e come me scommetto molti lettori confusi. Per il mio caso avevo anche avuto il requisito di far funzionare i miei dotfile su più piattaforme con diverse versioni di Git. Anch'io no alias g=gitma invece l'ho fattog definito una funzione.

Per raggiungere questo obiettivo ho dovuto mettere insieme diverse risposte qui in un'unica soluzione. Anche se questo ribadisce già le risposte, ho pensato che qualcuno nella mia barca potesse trovare questa raccolta utile come avrei fatto quando sono arrivato a questa domanda.

Ciò presuppone il completamento Git più vecchio e più recente, le impostazioni predefinite di Ubuntu e brew install gitsu MacOS. Nel caso successivo i completamenti installati sulla birra non venivano elaborati da bash (qualcosa che verrà diagnosticato in seguito).

# 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

0

Se usi alias g='git', aggiungo questa riga di codice.bash_aliases

complete -o default -o nospace -F _git g
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.