Как получить завершение bash для работы с псевдонимами?


195

Дело в точке:

Я на Mac с Bash v3.2.17, я использую Git, установленный через MacPort с вариантом Bash_completion.

Когда я печатаю git checkout m<tab>. например, я получил его до master.

Тем не менее, у меня есть псевдоним git checkout, gco. Когда я набираю текст gco m<tab>, я не получаю название автозаполнения.

В идеале я хотел бы, чтобы автозаполнение просто волшебным образом работало для всех моих псевдонимов. Является ли это возможным? В противном случае я бы хотел вручную настроить его для каждого псевдонима. Итак, как мне поступить?


3
завершено -o по умолчанию -o nospace -F в настоящее время не работает
восьмое

5
Вопросы с большим количеством откликов, чем в верхнем ответе, часто подразумевают большие запросы
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

2
Другой ответ от суперпользователя, когда кто-то указал мне, что мой вопрос был обманом этого. superuser.com/questions/436314/…
dstarh

Ответы:


183

Как указано в комментариях выше,

complete -o default -o nospace -F _git_checkout gco

больше не будет работать. Тем не менее, __git_completeв git-complete.bash есть функция, которую можно использовать для настройки завершения для псевдонимов, например:

__git_complete gco _git_checkout

6
Это единственный правильный ответ, который я видел среди многих неправильных.
восемь глаз

45
Если вы используете глобальный псевдоним "g" для git, вы также можете добавить, __git_complete g __git_mainчтобы завершить код, работая со всеми командами git.
Ондрей Мачулда

5
^^ Для новичков в git / shell / bash. Приведенный выше комментарий относится к псевдониму глобальной оболочки, а не к псевдониму git.
Элайджа Линн

14
Где я должен положить это?
benregn

15
Наконец-то разобрался, как это сделать правильно! Шаг 1) Скопируйте git-completion.bashиз <your git install folder>/etc/bash-completion.d/в ~/.git-completion.bash Шаг 2) добавьте source ~/.git-completion.bashв свой .bash_profile Шаг 3) Добавьте в __git_complete gco _git_checkoutлюбом месте после строки выше в вашем .bash_profile. Шаг 4) Перезагрузите оболочку и наслаждайтесь псевдонимом автозаполнения! :)
kpsfoo

54

Я столкнулся с этой проблемой и придумал этот фрагмент кода. Это автоматически даст вам завершение для всех псевдонимов. Запустите его после объявления всех (или любых) псевдонимов.

# 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
линия let COMP_CWORD+=$num_alias_argumentsпочему-то не работала на Mac OS X. Заменив его ((COMP_CWORD+=$num_alias_arguments))исправленным, хотя
Mario F

5
Вау, это круто - спасибо! wrap_aliasудушение двойных кавычек в определении псевдонима, и я думаю, что это не имеет особого смысла для псевдонимов с несколькими командами ( alias 'foo=bar; baz'), поэтому я ставлю дополнительные | grep -v '[";|&]'после alias -p. Кроме того, он становится немного медленным для сотен определений псевдонимов, но я рад подтвердить, что использование echoвместо передачи evalданных в файл кэша (который затем может быть evalотредактирован за один раз) работает нормально и работает очень быстро ,
Джо Лисс

2
Еще одна подсказка: wrap_aliasтребует настройки дополнений, поэтому мне пришлось двигаться source /etc/bash_completionвпереди wrap_aliasкода.
Джо Лисс

2
Это работало для меня на OS X 10.7.2 после изменения строки let COMP_CWORD+=$num_alias_argumentsна let \"COMP_CWORD+=$num_alias_arguments\".
IRH

7
Смотрите обновленную версию этого скрипта на сайте superuser.com/a/437508/102281 (например, я добавил поддержку COMP_LINE и COMP_POINT, которые требуются для некоторых завершений git).
Джон Меллор

18

Там git-completion.bashесть строка:

complete -o default -o nospace -F _git git

Глядя на эту строку (и функцию _git), вы можете добавить эту строку в ваш .bash_profile:

complete -o default -o nospace -F _git_checkout gco

4
некоторые функции git * bash больше не работают с этим методом
cmcginty

Да, раньше это прекрасно работало, пока что-то не изменилось в git_completion.bash ... Теперь он работает с полной командой, но не с псевдонимом.
Майкл Смит

Смотрите в конце этой страницы ответы, которые работают в современном git.
восемь глаз

Разве мы не должны изменить принятый ответ на «правильный» или хотя бы обновить принятый ответ, чтобы отразить это изменение?
Тони К.

это работает хорошо - добавил это в мой .bash_profile и до сих пор отлично работает с псевдонимами и без них: github.com/larrybotha/dotfiles/blob/master/…
Ларри

15

У меня есть псевдоним g = 'git', и в сочетании с псевдонимами git я набираю такие вещи, как

$ g co <branchname>

Более простое исправление для моего конкретного случая использования состояло в добавлении одной строки в git-complete.

Прямо под этой линией:

__git_complete git _git

Я добавил эту строку для обработки моего единственного псевдонима 'g':

__git_complete g _git

2
(Я использую Cygwin.) Я не смог найти файл git-completionили эту строку /etc/bash_completion.d/git, но я добавил complete -o default -o nospace -F _git gпосле своего псевдонима, .bash_aliasesи это сработало!
idbrii

Помните, что если вы отредактируете файл в /etc/bash-completion.d/или заново /usr/share/bash-completion/, вы потеряете свои изменения всякий раз, когда этот файл обновляется с помощью вашего менеджера пакетов.
kub1x

14

В идеале я хотел бы, чтобы автозаполнение просто волшебным образом работало для всех моих псевдонимов. Является ли это возможным?

Да, это возможно с проектом полного псевдонима (в Linux). Поддержка Mac является экспериментальной, но пользователи сообщают об успехе.


4
Большое спасибо, это намного лучше, чем выяснить, как каждая утилита в мире реализует завершение bash.
artm

2
Действительно, сэкономил мне время на настройку дополнений для псевдонимов.
Самир Алаймович

2
Работает как прелесть в Linux (не тестируется на Mac). Спасибо, что написали это!
битовая

1
Это просто потрясающе! Это просто работает, без суеты, намного лучше! Спасибо!
ЭЙ

5

Вы также можете попробовать использовать псевдонимы Git. Например, в моем ~/.gitconfigфайле есть раздел, который выглядит так:

[alias]
        co = checkout

Таким образом, вы можете напечатать git co m<TAB>, и это должно расшириться доgit co master , который является git checkoutкомандой.


5

Эта страница форума показывает решение.

Поместите эти строки в ваш .bashrcили .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

Это решение похоже на сценарий Бальшецера , но только этот на самом деле работает для меня. (У сценария Бальшецера были проблемы с некоторыми из моих псевдонимов.)


Это почти работает - я получаю пару ошибок, но завершение проходит. Что-нибудь еще, что я могу сделать? -bash: eval: line 28: unexpected EOF while looking for matching ''' -bash: eval: line 29: syntax error: unexpected end of file
pforhan

@pforhan Я вижу проблемы с цитированием выше ... "кавычки внутри functionстроки должны быть заключены в кавычки \". Это, вероятно, съедает одну из ваших 'цитат где-то вдоль строки.
Том Хейл,

5

Еще один вариант - использовать ~/.bash_completionфайл. Чтобы создать gcoпсевдоним для git checkoutпросто поместите это там:

_xfunc git __git_complete gco _git_checkout

Тогда ~/.bashrcвы должны указать только псевдоним:

alias gco='git checkout'

Две строчки Вот и все.

Объяснение:

~/bash_completionПолучает источников в конце основного bash_completion сценария. В Gentoo я нашел основной скрипт в/usr/share/bash-completion/bash_completion .

_xfunc gitНемного заботится о снабжаем git-completionфайл для вас , так что вам не нужно положить что - нибудь еще в ~/.bashrc.

Принятый ответ требует, чтобы вы скопировали .git-completion.shи отправили его из вашего ~/.bashrcфайла, который я нахожу хромым.


PS: я все еще пытаюсь понять, как не использовать весь git-completionсценарий в моей среде bash. Пожалуйста, прокомментируйте или отредактируйте, если найдете способ.


Почему _xfunc gitтребуется?
Том Хейл,

@ TomHale Я пытался улучшить ответ. Вместо того, чтобы делать, source ~/.git-completion.shя позволю _xfuncсделать это для меня. Просто приятнее и чище делать это только в ~/.bash_completion. Без _xfunc(или источников) __git_completeфункция не существует.
kub1x

1
Нет необходимости в ~/.bash_completionфайле - _xfuncлиния работает для меня в .bashrc.
Том Хейл,

2

Вам просто нужно найти complete команду и продублировать строку с псевдонимом.

У меня есть alias d-m="docker-machine". На словах d-mдолжен быть псевдоним дляdocker-machine .

Так на Mac (через brew) файлы завершения находятся в cd `brew --prefix`/etc/bash_completion.d/.
Для моего случая я отредактировал файл с именем docker-machine.
Весь путь внизу был:

complete -F _docker_machine docker-machine

Поэтому я просто добавил еще одну строку с моим псевдонимом:

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

Это лучшее решение для простых (один к одному) псевдонимов, таких как dockeraliased to d. Хотя для примера в вопросе git checkoutпсевдоним gcoболее сложен.
Висбуки

1

Сначала посмотрите исходную команду завершения. Пример:

$ complete | grep git

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

Теперь добавьте их в свой скрипт запуска (например, ~ / .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_loaderЛиния может не потребоваться. Но в некоторых ситуациях функция завершения загружается динамически только после ввода команды и TABпервого нажатия . Так что, если вы не использовали исходную команду и попробовали псевдоним + TAB, вы можете получить сообщение об ошибке типа «bash: завершение: функция« _docker »не найдена».


1

Есть много ответов на этот вопрос, и я, как и я, держу пари, что многие смущенные читатели. В моем случае у меня также было требование, чтобы мои dotfiles работали на нескольких платформах с разными версиями Git. Я тоже нет, alias g=gitно вместо этогоg определил как функцию.

Чтобы добиться этого, мне пришлось собрать воедино разные ответы в одном решении. Хотя это повторяет ответы уже, я думал, что кто-то в моей лодке мог бы счесть этот сборник полезным, как я сделал бы, когда я впервые пришел к этому вопросу.

Это предполагает более старое и новое завершение Git, Ubuntu по умолчанию и brew install gitв MacOS. В последнем случае установленные дополнения brew не обрабатывались bash (что я буду диагностировать позже).

# 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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.