我正在尝试为可能需要较长选项的命令编写Bash完成脚本,格式为--option
或--param=value
。如果用户已经在命令行上输入了一个选项,则该选项应从完成列表中排除(假设仅在命令行上一次指定给定选项才有意义)。
这里是第一次尝试:
_myprog()
{
local cur="${COMP_WORDS[$COMP_CWORD]}"
local words=(--help --param1= --param-state --param2=)
_exclude_cmd_line_opts
COMPREPLY=( $(compgen -W "${words[*]}" -- "$cur") )
}
complete -F _myprog myprog
_exclude_cmd_line_opts() {
local len=$(($COMP_CWORD - 1))
local i
for i in "${COMP_WORDS[@]:1:$len}" ; do
[[ $i == --* ]] && words=( "${words[@]/$i}" )
done
}
如果源此脚本为source script.sh
,然后编写:
$ myprog --param1= <tab><tab>
我得到以下完成列表:
= --help --param2= --param-state
所以它几乎可以正常工作,除了在完成列表中出现一个虚假的'='
符号。有什么建议吗?
由于默认内容COMP_WORDBREAKS
,因此在命令行上输入等号会导致单词中断。效果似乎是等号在COMP_WORDS
中作为单独的单词输入。在_exclude_cmd_line_opts
的以下修改中利用了这一点:
_exclude_cmd_line_opts() {
local len=$(($COMP_CWORD - 1))
local i
for ((i=1 ; i<=len; i++)) ; do
local j="${COMP_WORDS[$i]}"
if [[ $j == --* ]] ; then
(( i<len )) && [[ ${COMP_WORDS[$(( i + 1))]} == '=' ]] && j="$j="
words=( "${words[@]/$j}" )
fi
done
}
_exclude_cmd_line_opts
原始版本的问题是,例如${words[@]/$j}
和=
时,words=(param1=)
会给出伪造的j="param1"
(请注意$j
中缺少的尾随等号,这是由[ C0])..
更新
我发现了另一个特殊之处。上面的情况很好用,因为我不必在COMP_WORDBREAKS
符号后立即输入<tab>
。但是,例如,如果=
且我键入words=(--param= --param-info)
,则仍有两个候选补全,并且当前单词仅部分完成以成为--par<tab>
。此时,我想选择两个候选者中的第一个,然后在命令行上键入一个显式的--param
符号,然后键入=
,现在发生的是Bash认为您已经键入了空格(因为COMP_WORDBREAKS包含<tab>
),当前完成字从=
变为--param=
。再次,这将使Bash readline省略插入通常的空格,因此用户被迫键入空格以继续完成下一个选项。
通过返回带有空字符串的=
数组,可以避免在上述情况下必须输入空格。
COMPREPLY
此解决方案利用了_myprog()
{
local cur="${COMP_WORDS[$COMP_CWORD]}"
local prev=""
(( COMP_CWORD > 0 )) && prev="${COMP_WORDS[$(( COMP_CWORD - 1))]}"
[[ $cur == '=' && $prev == --* ]] && { COMPREPLY=( "" ); return; }
local words=(--param= --param-info)
_exclude_cmd_line_opts
COMPREPLY=( $(compgen -W "${words[*]}" -- "$cur") )
}
功能。它不会为采用此选项的选项填写参数。它还接受格式为_init_completion
的长选项。这不会检查光标位置后的行是否包含排除选项,但是可以根据需要进行修改以考虑整个命令行。
--param value