我正在将 bash-completion 添加到现有工具中。
在有人传递-s
标志后,我想提供几个关于 bash-completion 的选项:
Closed
Feedback
"In Progress"
New
Rejected
Resolved
里面的空间"In Progress"
引起了问题。
#!/usr/bin/env bash
_remote-redmine()
{
local cur
COMPREPLY=()
cur=${COMP_WORDS[$COMP_CWORD]}
case "${COMP_WORDS[($COMP_CWORD-1)]}" in
-s)
s_opts=( "New" "In Progress" "Resolved" "Feedback" "Closed" "Rejected" )
COMPREPLY=( $( compgen -W "${s_opts[*]}" -- "$cur") )
;;
esac
return 0
}
complete -F _remote-redmine remote-redmine
当我运行该工具时,我得到:
$ remote-redmine -s <tab><tab>
Closed Feedback In New Progress Rejected Resolved
但我想得到:
$ remote-redmine -s <tab><tab>
Closed Feedback "In Progress" New Rejected Resolved
或者
$ remote-redmine -s <tab><tab>
Closed Feedback In\ Progress New Rejected Resolved
我尝试过的事情:
s_opts=( ... "In\ Progress" ... )
: 没有不同s_opts( ... "In\\ Progress" ...)
: 没有不同s_opts( ... "In\\\ Progress" ...)
: 两个选项是In\
和Progress
s_opts( ... "\"In Progress\"" ...)
: 没有不同compgen -o nospace ...
: 没有不同compgen -o noquote ...
: 没有不同至少
compgen
可以放In Progress
一行,但这并不能转化为complete
$ compgen -W 'New "In Progress"' New In Progress
转义额外的引号有助于
compgen
,但这并不能转化为complete
:$ compgen -W 'New "\"In Progress\""' New "In Progress"
转义空格三次确实有帮助,但仍然没有帮助;不能转化为
complete
:$ compgen -W 'New In\\\ Progress' New In\ Progress
我认为逃离该空间七次是有希望的,因为这样
complete
可以解决其中的一些问题,但这也没有帮助:$ compgen -W 'New In\\\\\\\ Progress' New In\\\ Progress ... $ remote-redmine -s <tab><tab> ... In\\\ New Progress ...
绕过
compgen
会起作用,但随后我会失去单选项卡上的自动填充和双选项卡上的过滤功能COMPREPLY=( ... 'In\ Progress' ... ) COMPREPLY=( ,,, '"In Progress"' ... )
答案1
这与分词和 IFS 的默认值有关,这并不奇怪。
在
compgen -W "${s_opts[*]}"
数组扩展使用 的第一个字符作为分隔符[*]
将数组元素连接到单个字符串。IFS
默认情况下它是空格,因此值中的空格和作为分隔符的空格之间的区别丢失了。幸运的是,compgen -W
将参数解释为由字符分隔的值的字符串,因此如果您只是更改为类似的值或值中未出现的任何其他内容,IFS
它可以正常工作。[*]
IFS
:
然后,在
COMPREPLY=( $( compgen ...) )
compgen
您隐式地分割了使用打印的字符串IFS
(并通过通配符传递它们),但compgen
似乎打印了专门由换行符分隔的值。通过分割来做到这一点需要设置IFS
换行符,但实际上更适合使用 来读取readarray
,因为它无论如何都使用换行符并且不会通配符。
所以,你可以这样做
_remote-redmine() {
local cur
local IFS=:
COMPREPLY=()
cur=${COMP_WORDS[$COMP_CWORD]}
case "${COMP_WORDS[($COMP_CWORD-1)]}" in
-s)
s_opts=( "New" "In Progress" "Resolves" "Feedback" "Closed" "Rejected" )
readarray -t COMPREPLY < <( printf "%q\n" $(compgen -W "${s_opts[*]}" -- "$cur"))
;;
esac
return 0
}
或者,由于换行符在两个地方都有效,因此您可以像IFS=$'\n'
以前一样设置并分割输出,但您仍然应该在函数的持续时间内禁用通配符,这是要保存和恢复的又一个全局状态。
答案2
这对我有用。我们不使用compgen
,而是循环数组并添加前缀为 的项目$cur
:
_remote-redmine()
{
local cur
local item
COMPREPLY=()
cur=${COMP_WORDS[$COMP_CWORD]}
case "${COMP_WORDS[($COMP_CWORD-1)]}" in
-s)
local s_opts=( "New" "In\ Progress" "Resolved" "Feedback" "Closed" "Rejected" )
for item in "${s_opts[@]}"; do
case "$item" in
( "$cur"* )
COMPREPLY+=( "$item" )
;;
esac
done
;;
esac
return 0
}
complete -F _remote-redmine remote-redmine
请注意我添加的反斜杠:"In\ Progress"
。这是该项目的字面部分。当您键入In
并点击时Tab,您将In\ Progress
进入命令行:转义符现在位于双引号之外,因此处于活动状态:它可以防止单词拆分。