假设我有一个名为“foo”的命令,它接受一个参数(“加密”或“解密”),然后是一个文件列表。我想编写一个bash完成脚本来帮助第一个参数,然后为其他参数启用标准文件完成。我能来的最接近的是:
complete -F _foo foo
function _foo()
{
local cmds cur
if [ $COMP_CWORD -eq 1 ]
then
cur="${COMP_WORDS[1]}"
cmds=("encrypt decrypt")
COMPREPLY=($(compgen -W "${cmds}" -- ${cur}) )
else
COMPREPLY=($(compgen -f ${COMP_WORDS[${COMP_CWORD}]} ) )
fi
}
这正确地执行了第一个参数,并将从当前目录中为后续目录选择一个文件名。但是说当前目录包含一个包含文件baz.txt的子目录栏。键入ba-TAB-TAB后,完成结果为“bar”(“r”后的空格),并准备选择下一个参数。我想要的是标准的bash完成,其结果是“bar /”(斜杠后没有空格),准备在子目录中选择一个文件。有没有办法解决这个问题?
答案 0 :(得分:20)
我知道这有点晚了,但我在这里找到了一个解决方案:https://stackoverflow.com/a/19062943/108105
基本上,您使用complete -o bashdefault -o default
,当您想恢复默认的bash完成时,请设置COMPREPLY=()
。这是一个例子:
complete -o bashdefault -o default -F _foo foo
_foo() {
local cur=${COMP_WORDS[COMP_CWORD]}
if (( $COMP_CWORD == 1 )); then
COMPREPLY=( $(compgen -W 'encrypt decrypt' -- "$cur") )
else
COMPREPLY=()
fi
}
答案 1 :(得分:12)
bash文档可能有点神秘。最简单的解决方案是改变完成函数绑定:
complete -o filenames -F _foo foo
这意味着该函数返回文件名(包括目录),并启用对结果的特殊处理。
(恕我直言,文档没有明确说明这有效地后处理COMPREPLY[]
由您的完成函数设置;以及一些-o
选项,包括在内的compgen
选项,当应用于 complete -o filenames -o bashdefault -F _foo foo
似乎没有效果。)
您可以使用以下方法更接近正常的bash行为:
$VARIABLE
让你“〜”完成回来。
上面有两个问题:
$
展开无效,\$
将变为$
,以便更好地将文件名与@host
匹配。同样compgen
扩展也不起作用。我发现处理此问题的唯一方法是处理_foo()
{
local cmds cur ff
if (($COMP_CWORD == 1))
then
cur="${COMP_WORDS[1]}"
cmds="encrypt decrypt"
COMPREPLY=($(compgen -W "$cmds" -- "$cur"))
COMPREPLY=( "${COMPREPLY[@]/%/ }" ) # add trailing space to each
else
# get all matching files and directories
COMPREPLY=($(compgen -f -- "${COMP_WORDS[$COMP_CWORD]}"))
for ((ff=0; ff<${#COMPREPLY[@]}; ff++)); do
[[ -d ${COMPREPLY[$ff]} ]] && COMPREPLY[$ff]+='/'
[[ -f ${COMPREPLY[$ff]} ]] && COMPREPLY[$ff]+=' '
done
fi
}
complete -o bashdefault -o default -o nospace -F _foo foo
输出,而不依赖于“文件名”后处理:
cmd
(我还删除了上面compgen
的多余数组,并使nospace
更健壮,并处理文件名中的空格或前导破折号。)
现在的缺点是,当你获得中间完成列表时(即当你点击 tab 两次以显示多个匹配项)时,你将看不到尾随/开启目录,并且因为{{1启用~ $ @
扩展将不会增加空间以使其被接受。
简而言之,我不相信你可以轻松地混合和匹配你自己的完成和完整的bash完成行为。