在自定义Bash补全中,资源之后,如何防止对fzf的递归调用?

编程语言 2026-07-12

我在为自己的一个项目编写一个我自己的小型补全框架,结果对~/.bashrc执行两次会破坏补全。

在我的~/.bashrc中,我为foo设置了fzf的路径补全:

# ~/.bashrc
...
_fzf_setup_completion path foo

我决定让foo具备一个标志--bar,并在文件~/foo_completion.sh中定义它。为了保留现有的fzf补全(以及它可能具备的其他补全),我解析当前的补全函数,调用它,并用--bar更新COMPREPLY

# ~/foo_completion.sh
__orig_foo_completion="$(complete -p foo 2>/dev/null)"
__orig_foo_completion_func="$(echo "$__orig_foo_completion" | sed -r 's/.*\s+-F\s+([a-zA-Z_]+)\s+[a-zA-Z_]+/\1/')"

_foo_comp() {
    "$__orig_foo_completion_func"
    COMPREPLY+=($(compgen -W '--bar'))
}

complete -F _foo_comp foo

为了让它生效,我把我的~/.bashrc改为在fzf完成补全之后对这个文件执行source。

# ~/.bashrc
_fzf_setup_completion path foo
source ~/foo_completion.sh

可用,现已同时拥有两种补全。


问题在于,当我再次对~/.bashrc执行source时,尝试对foo进行补全看起来像是递归调用,导致会话崩溃。

# Start session: ~/.bashrc has been sourced

# Test fzf completion: shows fzf completion
foo **[tab]

# Test custom completion: shows `--bar`
foo[tab]

# Now resource ~/.bashrc: crash
source ~/.bashrc
foo[tab]

这是正确的做法吗? 也尝试把顺序颠倒,先对我的补全文件执行source,然后再执行fzf的补全,但问题仍然存在。

解决方案

正如与 @muru的评论交流所述,为了避免_foo_comp自调用自身,我本应该使用一个守卫,检查它是否已经被声明。

下面是带有该守卫的版本:

# ~/foo_completion.sh
if declare -F _foo_comp >/dev/null; then
    return
fi

__orig_foo_completion="$(complete -p foo 2>/dev/null)"
__orig_foo_completion_func="$(echo "$__orig_foo_completion" | sed -r 's/.*\s+-F\s+([a-zA-Z_]+)\s+[a-zA-Z_]+/\1/')"

_foo_comp() {
    $__orig_foo_completion_func
    COMPREPLY+=($(compgen -W '--bar'))
}

complete -F _foo_comp foo

问题在于即使这样,终端仍然会崩溃,似乎还存在另一种递归。

为解决这个问题,fzf的补全必须在自定义补全源之后。

# ~/.bashrc
source ~/foo_completion.sh

eval "$(fzf --bash)"
_fzf_setup_completion path foo
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章