如何在bash中启用默认文件完成

时间:2013-01-25 00:49:19

标签: bash

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

2 个答案:

答案 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

让你“〜”完成回来。

上面有两个问题:

  • 如果您有一个名为“encrypt”或“decrypt”的目录,那么关键字的扩展将会增加一个尾随“/”
  • $展开无效,\$将变为$,以便更好地将文件名与@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完成行为。