【问题标题】:How to enable default file completion in bash如何在 bash 中启用默认文件完成
【发布时间】:2013-01-25 00:49:18
【问题描述】:

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

【问题讨论】:

    标签: bash


    【解决方案1】:

    我知道这有点晚了,但我在这里找到了解决方案: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 非常整洁。它恢复为 readline 默认值而不是 bash,并不是说它会影响 OP,但是您会丢失 bashdefault 补全($、~、@)。我认为compopt -o bashdefault (bash-4.0) 可以提供帮助。
    【解决方案2】:

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

    complete -o filenames -F _foo foo
    

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

    (恕我直言,文档没有明确说明这会有效地对您的完成函数设置的COMPREPLY[] 进行后处理;并且当应用于compgen 时,其中的一些-o 选项会出现没有效果。)

    您可以使用以下方法更接近正常的 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 完成行为。

    【讨论】:

    • ${COMPREPLY[@]/%/ } 加空格? Bash 是一种真正难听的咳嗽语言。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-13
    • 1970-01-01
    • 2021-05-21
    • 2018-06-18
    • 2013-06-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多