繁体   English   中英

如何获得 bash 完成以使用别名?

[英]How do I get bash completion to work with aliases?

举个例子:

我在 mac 上使用 bash v3.2.17,我正在使用通过 macports 安装的 git 和 bash_completion 变体。

当我输入git checkout m<tab>时。 例如,我把它完成到master

但是,我有一个别名git checkoutgco 当我键入gco m<tab>时,我没有自动完成分支名称。

理想情况下,我希望自动完成功能能够神奇地为我的所有别名工作。 是否可以? 如果做不到这一点,我想为每个别名手动自定义它。 那么,我的go怎么办呢?

正如上面的评论所述,

complete -o default -o nospace -F _git_checkout gco

将不再起作用。 但是,在 git-completion.bash 中有一个__git_complete函数,可用于为别名设置完成,如下所示:

__git_complete gco _git_checkout

我也遇到了这个问题并想出了这个代码片段。 这将自动为您完成所有别名。 在声明所有(或任何)别名后运行它。

# 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

git-completion.bash有一行:

complete -o default -o nospace -F _git git

查看该行(和 _git 函数),您可以将此行添加到.bash_profile

complete -o default -o nospace -F _git_checkout gco

理想情况下,我希望自动完成功能可以神奇地为我所有的别名工作。 是否可以?

是的,完全别名项目(在 Linux 上)是可能的。 对 Mac 的支持是实验性的,但用户报告说是成功的。

我给 g='git' 取了别名,并结合我的 git 别名输入了诸如

$ g co <branchname>

对于我的特定用例,更简单的解决方法是向 git-completion 添加一行。

就在这条线的正下方:

__git_complete git _git

我添加了这一行来处理我的单个“g”别名:

__git_complete g _git

~/.bash_completion一种选择是使用~/.bash_completion文件。 要为git checkout创建gco别名,只需将其放在那里:

_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 bit 负责为您获取git-completion文件,因此您无需在~/.bashrc放入任何其他内容。

接受的答案要求您复制.git-completion.sh并从我发现蹩脚的~/.bashrc文件中获取它。


PS:我仍在试图弄清楚如何不将整个git-completion脚本源到我的 bash 环境中。 如果您找到方法,请发表评论或编辑。

您也可以尝试使用 Git 别名。 例如,在我的~/.gitconfig文件中,我有一个如下所示的部分:

[alias]
        co = checkout

所以你可以输入git co m<TAB> ,这应该扩展为git co master ,这是git checkout命令。

这个论坛页面显示了一个解决方案。

将这些行放入您的.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

这个解决方案类似于balshetzer's script ,但只有这个对我有用 (balshetzer 的脚本对我的一些别名有问题。)

您只需要找到complete命令并复制具有别名的行。

我有alias dm="docker-machine" 换句话说, dm应该是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

首先,查找原始完成命令。 例子:

$ 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后才会动态加载。 因此,如果您还没有使用原始命令,并尝试使用 alias + TAB ,您可能会收到类似“bash: completion: function '_docker' not found”之类的错误。

这个问题有很多答案,和我一样,我敢打赌很多困惑的读者。 就我而言,我还要求让我的 dotfiles 在具有不同 Git 版本的多个平台上工作。 我也没有alias g=git而是将g定义为一个函数。

为了做到这一点,我不得不将不同的答案拼凑成一个解决方案。 虽然这已经重申了答案,但我认为我船上的某个人可能会发现这个汇编很有用,就像我第一次遇到这个问题时一样。

这假设旧的和新的 Git 完成,Ubuntu 默认,并且在 MacOS 上brew install git 在后一种情况下,bash 没有处理 brew 安装的完成(我稍后会诊断)。

# 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

如果您使用alias g='git' ,我会在.bash_aliases添加这行代码

complete -o default -o nospace -F _git g

Felipe Contreras已经非常热衷于 Git 补全功能(请参阅Git 2.30 中的 Zsh 补全)提出(对于 - 可能 - Git 2.31,2021 年第一季度)一个公共功能,这将有助于别名自动补全。

他的提议:

早在 2012 年,我就主张引入一个帮助程序,允许用户指定别名,例如:

 git_complete gf git_fetch

那时有一些_GIT_complete ,因为公共函数没有明确的指导方针( git_complete_git_complete_GIT_complete ),并且一些别名实际上不起作用。

快进到 2020 年,仍然没有关于公共功能的指南,这些别名仍然不起作用(即使我发送了修复程序)。

这并没有阻止人们使用这个显然需要设置自定义别名的功能(这个页面),实际上这是推荐的方式。

但是用户必须输入很麻烦:

 __git_complete gf _git_fetch

或者更糟:

 __git_complete gk __gitk_main

8年的时间足以停止等待完美的到来; 让我们定义一个实际用户友好的公共函数(具有相同的名称):

 __git_complete gf git_fetch __git_complete gk gitk

同时还保持向后兼容性。

逻辑是:

  1. 如果$2存在,直接使用
  2. 如果没有,检查__$2_main存在
  3. 如果没有,检查_$2存在
  4. 如果没有,则失败

您可以将Tab绑定到alias-expand-line并在~/.inputrc complete (其默认操作)。 为此,您首先需要将每个操作绑定到一个键,然后将它们链接在一起,如下所示:

"\M-z":alias-expand-line
"\M-x":complete
TAB:"\M-z\M-x"

您可以使用任何您喜欢的组合键,我使用 Meta 组合键,因为它是免费的。 有关更多信息,请参阅man 3 readline

现在,如果您打开一个新终端并输入别名:

gco m<TAB>

该行将转换为

git checkout master

当然,即使不涉及别名, Tab仍会照常工作。

我知道这个问题是关于 bash 的,但是如果你使用的是 zsh,这个对@hesky-fisher 答案的更改将会解决它。

# 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'\'' /')"

到:

# For each defined alias, extract the necessary elements and use them
# to call wrap_alias.
eval "$(alias | sd '^([^=]+)=(.+)' 'wrap_alias $1=$2')"

你需要有sd (这可以用sed代替,但我认为sd更好)

这样做是因为 zsh 没有alias -palias output in zsh doesn't output

alias <something>=<value>

就像 bash 一样,而是

<something>=<value>

顺便说一句,我将初始化代码放在.zlogin文件中

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM