12

假设我有一个名为“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/”(斜杠后没有空格),准备在子目录中选择一个文件。有没有办法得到它?

4

2 回答 2

21

我知道这有点晚了,但我在这里找到了解决方案: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
}
于 2014-04-03T03:31:20.007 回答
13

bash 文档可能有点神秘。最简单的解决方案是更改完成函数绑定:

complete -o filenames -F _foo foo

这意味着该函数返回文件名(包括目录),并且启用了对结果的特殊处理。

(恕我直言,文档没有明确说明这可以有效地COMPREPLY[]按照您的完成功能设置进行后处理;并且-o其中包含的一些选项在应用时compgen似乎没有效果。)

您可以使用以下方法更接近正常的 bash 行为:

 complete -o filenames -o bashdefault -F _foo foo

这会让你“~”完成回来。

然而,上面有两个问题:

  • 如果您有一个名为“encrypt”或“decrypt”的目录,那么关键字的扩展将增长一个尾随“/”
  • $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更加健壮并处理文件名中的空格或前导破折号。)

现在的缺点是,当您获得中间完成列表时(即,当您点击tab两次以显示多个匹配项时)您不会在目录上看到尾随 /,并且由于nospace启用,~ $ @扩展不会增加空间以导致它们被接受。

简而言之,我不相信你可以简单地混合搭配你自己的完成和完整的 bash 完成行为。

于 2013-01-25T14:55:07.947 回答