16

我正在为一个与 curl 共享文件上传语义的工具构建一个 bash 完成脚本。

使用 curl,您可以执行以下操作:

curl -F var=@文件

上传文件。

我的应用程序具有类似的语义,我希望能够在按下“@”后显示可能的文件。不幸的是,事实证明这很困难:

cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
if [[ "$cur" == @* && "$prev" == '=' ]]; then
    COMPREPLY=( $(compgen -f ${cur:1}) )
    return 0
fi 

因此,如果命令(到目前为止)以:

abc=@

将显示当前目录中的文件。

 var=@/usr/                                                                                                                       
 /usr/bin      /usr/games 

问题是如果我真的点击标签来完成,'@'就会消失!

 var=/usr/bin

所以看起来 bash 用选项卡 COMPREPLY 替换了整个当前单词。

避免这种情况的唯一方法是这样做:

        COMPREPLY=( $(compgen -f ${cur:1}) )
        for (( i=0; i<${#COMPREPLY[@]}; i++ )); 
        do 
            COMPREPLY[$i]='@'${COMPREPLY[$i]} 
        done                                                                                                                       

但是现在选项卡完成看起来很奇怪,至少可以说:

@/usr/bin      @/usr/games

无论如何显示正常的文件选项卡完成(没有'@'前缀)但在点击选项卡时保留'@'?

4

2 回答 2

5

所以,这引起了我的兴趣,所以我一直在阅读 bash 完成源(可在 /etc/bash_completion 获得)。

我遇到了这个变量:${COMP_WORDBREAKS},它似乎允许控制用于分隔单词的字符。

我也遇到了这个功能,_get_cword和互补的_get_pword,都被推荐用于代替${COMP_WORDS[COMP_CWORD]}${COMP_WORDS[COMP_CWORD-1]}

所以,把所有这些放在一起,我做了一些测试,这就是我想出的:这似乎对我有用,至少,希望它也对你有用:

# maintain the old value, so that we only affect ourselves with this
OLD_COMP_WORDBREAKS=${COMP_WORDBREAKS}
COMP_WORDBREAKS="${COMP_WORDBREAKS}@"

cur="$(_get_cword)"
prev="$(_get_pword)"

if [[ "$cur" == '=@' ]]; then
    COMPREPLY=( $(compgen -f ${cur:2}) )

    # restore the old value
    COMP_WORDBREAKS=${OLD_COMP_WORDBREAKS}
    return 0
fi

if [[ "$prev" == '=@' ]]; then
    COMPREPLY=( $(compgen -f ${cur}) )

    # restore the old value
    COMP_WORDBREAKS=${OLD_COMP_WORDBREAKS}
    return 0

fi

现在,我承认拆分if箱有点脏,肯定有更好的方法,但我需要更多的咖啡因。

此外,无论它对你有价值,我还发现了-P <prefix>to 的参数compgen,这将阻止你在调用之后遍历$COMPREPLY[*]}数组compgen,就像这样

COMPREPLY=( $(compgen -P @ -f ${cur:1}) )

不过,面对完整的解决方案,这有点多余。

于 2012-05-15T15:30:08.150 回答
0

尝试这个 ....

function test
{
  COMPREPLY=()
  local cur="${COMP_WORDS[COMP_CWORD]}"
  local opts="Whatever sort of tabbing options you want to have!"
  COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
}
complete -F "test" -o "default" "test"
# complete -F "test" -o "nospace" "sd" ## Use this if you do not want a space after the completed word
于 2012-05-15T23:29:22.687 回答