在实际发布到这里之前,我已经非常努力地想弄清楚这个问题,但我似乎找不到任何其他人解决这个特定问题的例子。
我运行的是 Ubuntu 17.10
我编写了一个自定义函数来处理我的脚本之一的制表符完成。目的是,当我输入脚本名称并点击 [TAB] 时,它会列出 /opt/use 中以“.use”结尾的所有文件(不显示“.use”或前导路径) 。到目前为止我似乎已经做到了这一点。
/opt/use下的文件如下:
blender-2.79.use
chrome.use
clarisse-3.6.use
unity-2017.3.0p2.use
函数和完成的代码是:
_use () {
files=( `compgen -f -X "!*.use" /opt/use/` )
output=()
for file in "${files[@]}"; do
fileBaseName=`basename $file .use`
output+=("$fileBaseName")
done
COMPREPLY=( ${output[@]} )
}
complete -F _use use
请不要太严厉地评价我,我是一名图形艺术家,而不是程序员。 :)
另外,我的“use”脚本实际上是以下命令的别名:
source /opt/scripts/use.sh
现在当我输入:
use[SPACE][TAB][TAB]
我成功获得了 /opt/use 中以“.use”结尾的文件列表。
到目前为止,一切都很好。例如,我输入“use[SPACE][TAB][TAB]”,它是这样的:
bvz@bvz-xps15-linux:~$ use
blender-2.79 chrome clarisse-3.6 unity-2017.3.0p2
我的第一个问题是为什么我必须按两次 [TAB]?第一次只是发出嘟嘟声。第二次它向我展示了我的选择。这对我来说不是问题,我只是想知道这是否是我的问题的线索,即:
如果我输入足够多的字母以使其完全唯一,则制表符补全实际上并不会“补全”该行。它只是让条目与我键入的内容完全相同,并重新显示 /opt/use 中的文件列表。因此,例如,如果我输入:
use clar[TAB][TAB]
而不是填写行来读取:
use clarisse-3.6
这是我所期望的(也是我所追求的),它将行保留为:
use clar
并向我展示了可能完成的完整列表。这是一个示例:
bvz@bvz-xps15-linux:~$ use clar[TAB][TAB]
blender-2.79 chrome clarisse-3.6 unity-2017.3.0p2
bvz@bvz-xps15-linux:~$ use clar
请注意,它实际上并未完成读取“clarisse-3.6”的行,即使这是唯一可能的完成。
谁能告诉我我做错了什么?另外,如果这是重复的,我深表歉意。我已经环顾了好几天,但找不到任何有人遇到这个问题的例子,更不用说解决它了。
谢谢
答案1
感谢 Muru 为我指出了解决方案。
显然我的问题是每次调用我的函数时它总是返回可能匹配的完整列表。我只需要返回那些与用户迄今为止输入的内容相匹配的可能的完成结果。一旦我只返回一个项目,那么 bash 将自动完成该行的其余部分。
这是有效的功能。在添加每个可能的完成之前,我会快速检查一下完成是否以最初键入的单词开头。我使用正则表达式来做到这一点。我还必须测试一下他们是否还没有输入任何内容 - 在这种情况下我总是返回所有内容。
我还更改了它以使更多变量成为“本地”变量。
_use () {
local word="${COMP_WORDS[$COMP_CWORD]}"
local files=( `compgen -f -X "!*.use" /opt/use/` )
local output=()
for file in "${files[@]}"; do
fileBaseName=`basename $file .use`
if [[ $fileBaseName =~ ^$word.* ]] || [ "$word" == "" ]; then
output+=("$fileBaseName")
fi
done
COMPREPLY=( ${output[@]} )
}
答案2
现在我已经有一些时间考虑这个问题了,我认为完成函数可以简化为:
_use () {
local word="${COMP_WORDS[$COMP_CWORD]}"
local IFS=$'\n'
COMPREPLY=( $(find /opt/use/ -type f -name "$word*.use" -exec basename -as .use {} +) )
}
假设你的basename
新版本足以支持-a
,否则就这样做-exec basename {} .use \;
。