我正在为一个与 curl 共享文件上传语义的工具构建一个 bash 完成脚本。
使用 curl,您可以:
curl -F var=@file
上传文件。
我的应用程序具有相似的语义,我希望能够在按下“@”后显示可能的文件。不幸的是,这被证明是困难的:
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
问题是,如果我真的点击 Tab 键完成,“@”就会消失!
var=/usr/bin
所以看起来 bash 用制表符 COMPREPLY 替换了整个当前词。
避免这种情况的唯一方法是这样做:
COMPREPLY=( $(compgen -f ${cur:1}) )
for (( i=0; i<${#COMPREPLY[@]}; i++ ));
do
COMPREPLY[$i]='@'${COMPREPLY[$i]}
done
但现在 Tab 补全看起来至少可以说很奇怪:
@/usr/bin @/usr/games
有没有显示正常的文件选项卡完成(没有“@”前缀)但在点击选项卡时保留“@”?
最佳答案
所以,这引起了我的兴趣,所以我一直在通读 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
现在,我承认 split if
箱子有点脏,肯定有更好的方法,但我需要更多咖啡因。
此外,不管它对你有什么值(value),我还发现了 -P <prefix>
compgen
的参数,这将防止您不得不遍历 $COMPREPLY[*]}
调用后的数组 compgen
, 像这样
COMPREPLY=( $(compgen -P @ -f ${cur:1}) )
不过,面对完整的解决方案,这有点多余。
关于bash - 制表符完成时如何防止 bash 完成替换字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10493505/