当命令需要包含冒号时,我在创建Bash完成函数时遇到了问题。当您键入命令并按下选项卡时,Bash将命令行的内容插入到数组中,只有这些数组被冒号拆分。所以命令:
哑福:苹果
将变成:(“傻瓜”:“苹果”)
我知道一个解决方案是更改COMP_WORDBREAKS,但这不是一个选项,因为这是一个团队环境,在这里,我可能会破坏其他代码的混乱与COMP_WORDBREAKS。
然后,this answer建议使用_get_comp_words_by_ref和__ltrim_colon_completions变量,但从如何使用这些变量的答案来看,我一点也不清楚。
所以我在下面尝试了一个不同的解决方案。基本上,将命令行读入字符串,并通过计算“偏移量”计算出用户光标当前选择的单词。如果命令行中有一个冒号,文本位于命令行的左边或右边,它将在偏移量中各添加一个,然后从COMP_CWORD变量中减去这一点。
1 #!/bin/bash
2 _comp() {
3 #set -xv
4 local a=("${COMP_WORDS[@]}")
5 local index=`expr $COMP_CWORD`
6 local c_line="$COMP_LINE"
7
8 # Work out the offset to change the cursor by
9 # This is needed to compensate for colon completions
10 # Because COMP_WORDS splits words containing colons
11 # E.g. 'foo:bar' becomes 'foo' ':' 'bar'.
12
13 # First delete anything in the command to the right of the cursor
14 # We only need from the cursor backwards to work out the offset.
15 for ((i = ${#a[@]}-1 ; i > $index ; i--));
16 do
17 regex="*([[:space:]])"${a[$i]}"*([[:space:]])"
18 c_line="${c_line%$regex}"
19 done
20
21 # Count instances of text adjacent to colons, add that to offset.
22 # E.g. for foo:bar:baz, offset is 4 (bar is counted twice.)
23 # Explanation: foo:bar:baz foo
24 # 0 12 34 5 <-- Standard Bash behaviour
25 # 0 1 <-- Desired Bash behaviour
26 # To create the desired behaviour we subtract offset from cursor index.
27 left=$( echo $c_line | grep -o "[[:alnum:]]:" | wc -l )
28 right=$( echo $c_line | grep -o ":[[:alnum:]]" | wc -l )
29 offset=`expr $left + $right`
30 index=`expr $COMP_CWORD - $offset`
31
32 # We use COMP_LINE (not COMP_WORDS) to get an array of space-separated
33 # words in the command because it will treat foo:bar as one string.
34 local comp_words=($COMP_LINE)
35
36 # If current word is a space, add an empty element to array
37 if [ "${COMP_WORDS[$COMP_CWORD]}" == "" ]; then
38 comp_words=("${comp_words[@]:0:$index}" "" "${comp_words[@]:$index}" )
39 fi
40
41
42 local cur=${comp_words[$index]}
43
44 local arr=(foo:apple foo:banana foo:mango pineapple)
45 COMPREPLY=()
46 COMPREPLY=($(compgen -W "${arr[*]}" -- $cur))
47 #set +xv
48 }
49
50 complete -F _comp dummy 问题是,这仍然不能正常工作。如果我输入:
dummy pine<TAB>然后它将正确地完成dummy pineapple。如果我输入:
dummy fo<TAB>然后,它将显示三个可用的选项,foo:apple foo:banana foo:mango。到目前一切尚好。但如果我打字:
dummy foo:<TAB>然后我得到的输出是dummy foo:foo:,然后进一步的选项卡不起作用,因为它将foo:foo:解释为cur,这与任何完成都不匹配。
当我单独测试compgen命令时,如下所示:
compgen -W 'foo:apple foo:banana foo:mango pineapple' -- foo:然后返回三个匹配结果:
foo:apple
foo:banana
foo:mango因此,我假设Bash看到它有一个空字符串和三个可供完成的候选项,因此将前缀foo:添加到命令行的末尾--尽管foo:已经是要完成的游标。
我不明白的是怎么解决这个问题。当结肠不涉及,这工作很好-“松树”将永远完成菠萝。如果我去更改数组以添加更多选项:
local arr=(foo:apple foo:banana foo:mango pineapple pinecone pinetree)
COMPREPLY=()
COMPREPLY=($(compgen -W "${arr[*]}" -- $cur))然后,当我输入dummy pine<TAB>时,它仍然很高兴地向我展示了pineapple pinecone pinetree,并且没有试图在最后添加一个多余的pine。
这种行为有什么解决办法吗?
发布于 2022-03-09 14:09:47
过去对我有用的一种方法是用单引号包装compgen的输出,例如:
__foo_completions() {
COMPREPLY=($(compgen -W "$(echo -e 'pine:cone\npine:apple\npine:tree')" -- "${COMP_WORDS[1]}" \
| awk '{ print "'\''"$0"'\''" }'))
}
foo() {
echo "arg is $1"
}
complete -F __foo_completions foo然后:
$ foo <TAB>
$ foo 'pine:<TAB>
'pine:apple' 'pine:cone' 'pine:tree'
$ foo 'pine:a<TAB>
$ foo 'pine:apple'<RET>
arg is pine:apple
$ foo pi<TAB>
$ foo 'pine:https://stackoverflow.com/questions/65233239
复制相似问题