Як змусити баш завершити роботу з псевдонімами?


195

Справа в точці:

Я на mac з bash v3.2.17, я використовую git, встановлений через macports з варіантом 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-fill.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))фіксованим, хоча
Маріо Ф

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/…
Larry

15

У мене є псевдонім g = 'git', і в поєднанні зі своїми git псевдонімами я набираю такі речі

$ g co <branchname>

Найпростішим виправленням мого конкретного випадку використання було додавання одного рядка до завершення git.

Прямо під цією лінією:

__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
велике спасибі, це набагато краще, ніж з'ясувати, як кожна утиліта у світі реалізує завершення баш.
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 (через вариво) файли завершення знаходяться в 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

Це найкраще рішення для простих (один на один) псевдонімів, на зразок dockerпсевдоніму d. Хоча для прикладу у питанні, git checkoutпсевдонім gcoє складнішим.
wisbucky

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. У більш пізньому випадку встановлені варіння комплекти не оброблялися 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

0

Якщо ви використовуєте alias g='git', я додаю цей рядок коду в.bash_aliases

complete -o default -o nospace -F _git g
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.