UsA*_*R33 16 bash bash-completion
我正在为一个工具构建一个bash完成脚本,该脚本与curl共享文件上传语义.
使用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
Run Code Online (Sandbox Code Playgroud)
因此,如果命令(到目前为止)以以下结尾:
abc=@
Run Code Online (Sandbox Code Playgroud)
将显示当前目录中的文件.
var=@/usr/
/usr/bin /usr/games
Run Code Online (Sandbox Code Playgroud)
问题是,如果我真的按Tab键完成,'@'就会消失!
var=/usr/bin
Run Code Online (Sandbox Code Playgroud)
所以看起来bash用标签式COMPREPLY替换整个当前单词.
避免这种情况的唯一方法是这样做:
COMPREPLY=( $(compgen -f ${cur:1}) )
for (( i=0; i<${#COMPREPLY[@]}; i++ ));
do
COMPREPLY[$i]='@'${COMPREPLY[$i]}
done
Run Code Online (Sandbox Code Playgroud)
但是现在标签的完成看起来很奇怪:
@/usr/bin @/usr/games
Run Code Online (Sandbox Code Playgroud)
反正有没有显示正常的文件标签完成(没有'@'前缀)但在点击标签时保留'@'?
所以,这引起了我的兴趣,所以我一直在阅读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
Run Code Online (Sandbox Code Playgroud)
现在,我承认分裂的if
情况有点脏,肯定有更好的方法,但我需要更多的咖啡因.
此外,无论它对你有什么价值,我还发现了-P <prefix>
参数的方式compgen
,这将阻止你$COMPREPLY[*]}
在调用之后不得不循环遍历数组compgen
,就像这样
COMPREPLY=( $(compgen -P @ -f ${cur:1}) )
Run Code Online (Sandbox Code Playgroud)
但是,面对完整的解决方案,这有点多余.