【问题标题】:How to give correct suggestions to tab complete when my words contains colons当我的单词包含冒号时如何为制表符提供正确的建议
【发布时间】:2015-02-12 13:52:41
【问题描述】:

我正在为一个实用程序编写一个 bash 选项卡完成文件,该实用程序有时需要表单上的完整 URL:protocol://host:port。这包含两个冒号,这已被证明对于制表符完成存在问题。这是因为冒号被视为分词。我读过我不应该直接更改COMP_WORDBREAKS,所以我想按照这里的建议使用_get_comp_words_by_ref__ltrim_colon_completionsHow to reset COMP_WORDBREAKS without effecting other completion script?

这适用于单个冒号,但第二个冒号会导致一个小问题,如以下最小示例所示:

这个例子说明了这个问题。它出现在建议中的任意数量的冒号上。

[root@2e3e8853cc0c /]# cat /etc/bash_completion.d/foo 
_foo()
{
    local cur
    COMPREPLY=()
    _get_comp_words_by_ref -n : -c cur

    COMPREPLY=( $(compgen -W "http://host:1234/aaa http://host:1234/bbb http://host:1234/ccc" -- ${cur}) )
    __ltrim_colon_completions "$cur"
    return 0
}
complete -F _foo foo

foo 成功完成公共部分后点击标签。之后点击两次选项卡,会产生以下建议:

[root@2e3e8853cc0c /]# foo http://host:1234/
1234/aaa  1234/bbb  1234/ccc

想要的结果当然是:

[root@2e3e8853cc0c /]# foo http://host:1234/
http://host:1234/aaa  http://host:1234/bbb  http://host:1234/ccc

之后,点击 a、b 或 c 加标签按预期工作,它完成了完整的 URL。

关于如何产生正确的输出有什么建议吗?是否需要手动更改COMPREPLY 变量,还是我只是使用了错误的函数?

【问题讨论】:

  • 我已经从COMP_WORDBREAKS 中删除了@:=,我可以不用它们。 :)
  • 我也可以不用它们,但是这个实用程序将安装在多个主机上,而不仅仅是我的。使用 scp 时,远程主机和文件路径之间有一个冒号是正常的,如果我删除冒号,这将表现不同。
  • 每当我想要在:(或=)之后自动完成文件名时,我只需在: 之后输入SPACE。文件名完成后,我会将光标移回并删除SPACE。我正在使用 Bash 的 vi 编辑模式,所以这几乎不会增加任何成本。
  • 当您修改自己的环境时这没问题,但我不想在我的包中添加这样的 hack。我需要一种在不改变系统中其他工具的自动完成行为的情况下解决我的问题的方法。

标签: bash bash-completion


【解决方案1】:

我想出了一个基于我一直使用的技巧的解决方案。希望它会有所帮助。

_bar()
{
    local CUR=$2
    local cur
    local -a compreply=()
    local -a urls=(ftp://gnu.org \
                   http://host1:1234/aaa \
                   http://host2:1234/bbb \
                   http://host2:1234/ccc)

    _get_comp_words_by_ref -n : -c cur

    compreply=( $(compgen -W "${urls[*]}" -- "$cur") )
    COMPREPLY=( "${compreply[@]}" )
    __ltrim_colon_completions "$cur"

    if [[ ${#COMPREPLY[@]} -gt 1 ]]; then
        local common_prefix
        common_prefix=$( printf '%s\n' "${COMPREPLY[@]}" \
                         | sed '$q;N;s/^\(.*\).*\n\1.*$/\1/;h;G;D' )
        if [[ $common_prefix == "$CUR" ]]; then
            COMPREPLY=( "${compreply[@]}" " " )
        fi
    fi

    return 0
}

complete -F _bar bar

下面是它的样子(用Bash 4.3.33测试):

[STEP 101] $ bar <TAB><TAB>
                       http://host1:1234/aaa  http://host2:1234/ccc
ftp://gnu.org          http://host2:1234/bbb
[STEP 101] $ bar f<TAB>
[STEP 101] $ bar ftp://gnu.org␣
[STEP 101] $ bar ftp://gnu.org <ENTER>
bash: bar: command not found
[STEP 102] $ bar h<TAB>
[STEP 102] $ bar http://host
[STEP 102] $ bar http://host<TAB><TAB>
                       http://host2:1234/bbb
http://host1:1234/aaa  http://host2:1234/ccc
[STEP 102] $ bar http://host2<TAB>
[STEP 102] $ bar http://host2:1234/
[STEP 102] $ bar http://host2:1234/<TAB><TAB>
                       http://host2:1234/bbb  http://host2:1234/ccc
[STEP 102] $ bar http://host2:1234/b<TAB>
[STEP 102] $ bar http://host2:1234/bbb␣
[STEP 102] $ bar http://host2:1234/bbb <ENTER>
bash: bar: command not found
[STEP 103] $

实际上问题并不在于两个或多个冒号。一个冒号也有类似的问题。

【讨论】:

  • 谢谢!它似乎工作得很好。我将对其进行更多测试,看看是否能找到一些极端情况。你能解释一下代码中发生了什么吗?你对单冒号是正确的。我已经更新了问题。
  • 我还想了解更多关于这段代码是如何工作的,因为我遇到了与 OP 相同的问题,但不确定你是如何修复它的。你会考虑在方便的时候添加一些解释性的 cmets,pynexj?
  • @Lou 我自己从不使用bash-completion pkg。完全忘记了_get_comp_words_by_ref 之类的功能在做什么。你不明白哪个具体部分?
  • 我不清楚 _get_comp_words_by_ref__ltrim_colon_completions 在函数中实际做了什么。据我所知,它们都是在小写 cur 分配给任何东西之前被调用的。我很想知道他们如何改变 _bar() 函数的行为,以便它处理冒号补全。
  • 这些函数来自bash-completion pkg。你可以参考它的官方文档。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-08-09
  • 2015-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-12
  • 1970-01-01
相关资源
最近更新 更多