Jak uzyskać ukończenie bash do pracy z aliasami?

195

Przykładem:

Jestem na Macu z bash v3.2.17, używam git zainstalowanego przez macports z wariantem bash_completion.

Po wpisaniu git checkout m<tab>. na przykład, ukończę to master.

Jednak mam alias git checkout, gco. Kiedy piszę gco m<tab>, nazwa oddziału nie jest automatycznie uzupełniana.

Idealnie byłoby, gdyby autouzupełnianie działało magicznie dla wszystkich moich aliasów. Czy to możliwe? W przeciwnym razie chciałbym ręcznie dostosować go dla każdego aliasu. Więc jak mam to zrobić?

kch
źródło
3
zupełny -o domyślny -o nospace -F obecnie nie działa
eighteyes
5
Pytania o większej liczbie głosów pozytywnych niż najlepsza odpowiedź często oznaczają wspaniałe prośby o nowe funkcje
Ciro Santilli 20 冠状 病 六四 事件 法轮功
2
Kolejna odpowiedź superużytkownika, gdy ktoś wskazał mi, że moje pytanie było duplikatem tego. superuser.com/questions/436314/…
dstarh

Odpowiedzi:

183

Jak stwierdzono w komentarzach powyżej,

complete -o default -o nospace -F _git_checkout gco

przestanie działać. Jednak __git_completew git-uzupełnianie.bash jest funkcja, której można użyć do skonfigurowania zakończenia dla takich aliasów:

__git_complete gco _git_checkout
chris_sutter
źródło
6
To jedyna poprawna odpowiedź, jaką widziałem wśród wielu niewłaściwych.
eighteyes
45
Jeśli używasz globalnego aliasu „g” dla git, możesz również dodać, __git_complete g __git_mainaby uzyskać uzupełnianie kodu działające na wszystkich poleceniach git.
Ondrej Machulda
5
^^ Dla nowych użytkowników git / shell / bash. Powyższy komentarz odnosi się do globalnego aliasu powłoki, a nie natywnego aliasu git.
Elijah Lynn
14
Gdzie mam to położyć?
benregn
15
Wreszcie wymyśliłem, jak to zrobić poprawnie! Krok 1) Skopiuj git-completion.bashz <your git install folder>/etc/bash-completion.d/do ~/.git-completion.bash kroku 2) dodaj source ~/.git-completion.bashdo .bash_profile kroku 3) Dodaj __git_complete gco _git_checkoutdowolne miejsce po powyższej linii w pliku .bash_profile. Krok 4) Uruchom ponownie powłokę i ciesz się automatycznym uzupełnianiem aliasu! :)
kpsfoo,
54

Natknąłem się również na ten problem i wymyśliłem ten fragment kodu. Spowoduje to automatyczne uzupełnienie wszystkich aliasów. Uruchom go po zadeklarowaniu wszystkich (lub dowolnych) aliasów.

# 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
źródło
6
linia let COMP_CWORD+=$num_alias_argumentsz jakiegoś powodu nie działała na Mac OS X. Zastąpienie go ((COMP_CWORD+=$num_alias_arguments))naprawiono
Mario F
5
Wow, to niesamowite - dzięki! wrap_aliasdusi się w podwójnych cudzysłowach w definicji aliasu i myślę, że nie ma to większego sensu dla aliasów wielozadaniowych ( alias 'foo=bar; baz'), więc umieszczam dodatkowy | grep -v '[";|&]'po alias -p. Poza tym robi się trochę powolny w przypadku setek definicji aliasów, ale z przyjemnością potwierdzam, że użycie echozamiast evali przekazanie danych wyjściowych do pliku pamięci podręcznej (który można następnie eval„za jednym zamachem”) działa dobrze i jest super szybkie .
Jo Liss,
2
Kolejna wskazówka: wrap_aliaswymaga skonfigurowania uzupełnień, więc musiałem przejść source /etc/bash_completionprzed wrap_aliaskodem.
Jo Liss,
2
To działało dla mnie w OS X 10.7.2 po zmianie linii let COMP_CWORD+=$num_alias_argumentsna let \"COMP_CWORD+=$num_alias_arguments\".
irh
7
Zobacz zaktualizowaną wersję tego skryptu na superuser.com/a/437508/102281 (na przykład dodałem obsługę COMP_LINE i COMP_POINT, które są wymagane do niektórych uzupełnień git).
John Mellor,
18

W git-completion.bashistnieje linia:

complete -o default -o nospace -F _git git

Patrząc na tę linię (i funkcję _git) możesz dodać tę linię do .bash_profile:

complete -o default -o nospace -F _git_checkout gco
Chris Lloyd
źródło
4
niektóre funkcje git * bash nie działają już przy użyciu tej metody
cmcginty
Tak, to działało świetnie, dopóki coś się nie zmieniło w git_completion.bash ... Teraz działa z pełną komendą, ale nie z aliasem.
Michael Smith
Na końcu tej strony znajdziesz odpowiedzi, które działają w nowoczesnym git.
eighteyes
Czy nie powinniśmy zmienić przyjętej odpowiedzi na „poprawną”, a przynajmniej zaktualizować zaakceptowaną odpowiedź, aby odzwierciedlić zmianę?
Tony K.
to działa dobrze - dodaje to do mojego bash_profile i działa dobrze i bez aliasów do tej pory: github.com/larrybotha/dotfiles/blob/master/...
Larry
15

Alias ​​g = „git” i w połączeniu z aliasami git piszę takie rzeczy

$ g co <branchname>

Prostszym rozwiązaniem dla mojego konkretnego przypadku użycia było dodanie pojedynczej linii do git-complete.

Tuż poniżej tej linii:

__git_complete git _git

Dodałem tę linię, aby obsługiwać mój pojedynczy alias „g”:

__git_complete g _git
wonderfulthunk
źródło
2
(Używam Cygwin.) Nie mogłem znaleźć pliku git-completionani tego wiersza /etc/bash_completion.d/git, ale dodałem complete -o default -o nospace -F _git gpo moim aliasie .bash_aliasesi zadziałało!
idbrii
Uwaga: jeśli edytujesz plik w /etc/bash-completion.d/lub w nowym /usr/share/bash-completion/, utracisz zmiany za każdym razem, gdy plik zostanie zaktualizowany za pomocą menedżera pakietów.
kub1x
14

Idealnie byłoby, gdyby autouzupełnianie działało magicznie dla wszystkich moich aliasów. Czy to możliwe?

Tak, jest to możliwe dzięki projektowi z pełnym aliasem (w systemie Linux). Obsługa komputerów Mac jest eksperymentalna, ale użytkownicy zgłosili sukces.

Cyker
źródło
4
wielkie dzięki, jest to o wiele lepsze niż wymyślenie, jak każde narzędzie na świecie implementuje zakończenie bash.
artm
2
Rzeczywiście, zaoszczędziłem trochę czasu na konfigurowanie uzupełnień dla aliasów.
Samir Alajmovic
2
Działa jak urok w Linuksie (nie testowany na Macu). Dzięki za napisanie tego!
maska ​​bitowa
1
To jest po prostu niesamowite! To po prostu działa, bez zamieszania, znacznie lepiej! Dzięki!
emi
5

Możesz także spróbować użyć aliasów Git. Na przykład w moim ~/.gitconfigpliku mam sekcję, która wygląda następująco:

[alias]
        co = checkout

Więc możesz wpisać git co m<TAB>, a to powinno rozwinąć się do git co master, które jest git checkoutpoleceniem.

mipadi
źródło
5

Ta strona forum pokazuje rozwiązanie.

Umieść te linie w swoim .bashrclub .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

To rozwiązanie jest podobne do skryptu balshetzera , ale tylko to faktycznie działa dla mnie. (skrypt balshetzera miał problemy z niektórymi moimi pseudonimami).

hcs42
źródło
; To prawie działa - otrzymuję kilka błędów, ale proces dobiega końca. Coś jeszcze mogę zrobić? -bash: eval: line 28: unexpected EOF while looking for matching ''' -bash: eval: line 29: syntax error: unexpected end of file
pforhan
@pforhan Widzę problemy z cytowaniem powyżej ... "cytaty wewnątrz functionciągu powinny być cytowane jako \". To prawdopodobnie zjada jeden z twoich 'cytatów gdzieś wzdłuż linii.
Tom Hale
5

Jeszcze jedną opcją jest użycie ~/.bash_completionpliku. Aby utworzyć gcoalias, po git checkoutprostu wstaw to tutaj:

_xfunc git __git_complete gco _git_checkout

Następnie ~/.bashrcmusisz wstawić sam alias:

alias gco='git checkout'

Dwie linie. Otóż ​​to.

Wyjaśnienie:

~/bash_completionDostaje pozyskiwane na końcu głównego skryptu bash_completion. W Gentoo znalazłem główny skrypt /usr/share/bash-completion/bash_completion.

_xfunc gitNieco dba o zaopatrywaniu się git-completionplik dla Ciebie, dzięki czemu nie trzeba kłaść indziej ~/.bashrc.

Akceptowana odpowiedź wymaga skopiowania .git-completion.shi pobrania jej z ~/.bashrcpliku, który uważam za kiepski.


PS: Nadal próbuję wymyślić, jak nie pobierać całego git-completionskryptu do mojego środowiska bash. Skomentuj lub edytuj, jeśli znajdziesz sposób.

kub1x
źródło
Dlaczego jest to _xfunc gitwymagane?
Tom Hale,
@TomHale Próbowałem poprawić odpowiedź. Zamiast robić source ~/.git-completion.shNiech I _xfunczrobić to za mnie. To po prostu przyjemniejsze i czystsze, gdy robi się to wyłącznie w ~/.bash_completion. Bez _xfunc(lub pozyskiwania) __git_completefunkcja nie istnieje.
kub1x
1
~/.bash_completionPlik nie jest potrzebny - _xfunclinia działa dla mnie w .bashrc.
Tom Hale,
2

Musisz po prostu znaleźć completepolecenie i powielić wiersz o nazwie alias.

Mam alias d-m="docker-machine". Innymi słowy, d-mbędzie to pseudonim docker-machine.

Tak więc na Macu (przez napar) znajdują się pliki ukończenia cd `brew --prefix`/etc/bash_completion.d/.
W moim przypadku edytowałem plik o nazwie docker-machine.
Na dole było:

complete -F _docker_machine docker-machine

Właśnie dodałem kolejną linię z moim aliasem:

complete -F _docker_machine docker-machine
complete -F _docker_machine d-m
luckydonald
źródło
Jest to najlepsze rozwiązanie dla prostych aliasów (jeden do jednego), takich jak dockeralias do d. Chociaż dla przykładu w pytaniu, git checkoutalias do gcojest bardziej złożony.
wisbucky
1

Najpierw wyszukaj oryginalne polecenie zakończenia. Przykład:

$ complete | grep git

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

Teraz dodaj je do skryptu uruchamiania (np. ~ / .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

_completion_loaderMoże nie być wymagana linia. Ale w niektórych sytuacjach funkcja uzupełniania jest ładowana dynamicznie dopiero po wpisaniu polecenia i TABpierwszym naciśnięciu . Jeśli więc nie użyłeś oryginalnej komendy i wypróbowałeś alias + TAB, możesz otrzymać błąd typu „bash: complete: nie znaleziono funkcji” _docker ”.

wisbucky
źródło
1

Istnieje wiele odpowiedzi na to pytanie i podobnie jak ja stawiam na wielu zdezorientowanych czytelników. W moim przypadku miałem również wymaganie, aby moje pliki dot działały na wielu platformach z różnymi wersjami Git. Ja też nie, alias g=gitale zamiast tego gzdefiniowałem jako funkcję.

Aby to osiągnąć, musiałem połączyć różne odpowiedzi tutaj w jedno rozwiązanie. Chociaż to powtórzy już odpowiedzi, myślałem, że ktoś w mojej łodzi może uznać tę kompilację za przydatną, tak jak zrobiłbym to, kiedy po raz pierwszy zadałem to pytanie.

Zakłada to starsze i nowsze zakończenie Git, domyślne ustawienia Ubuntu i brew install gitna MacOS. W późniejszym przypadku uzupełnienia instalacji naparu nie były przetwarzane przez bash (coś zdiagnozuję później).

# 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
źródło
0

Jeśli używasz alias g='git', dodaję ten wiersz kodu.bash_aliases

complete -o default -o nospace -F _git g
Druta Ruslan
źródło