如何完成相对于另一个目录的文件名?

时间:2014-12-02 07:49:02

标签: bash autocomplete

这是以下讨论的后续问题:

https://bugs.launchpad.net/ubuntu/+source/bash-completion/+bug/1394920

假设我有一个文件夹~/tmp,其中包含一些文件和目录:

$ mkdir a; touch file1.txt; mkdir a/b; mkdir a/c; touch a/d; mkdir a/b/c

我现在尝试创建一个完成脚本来完成~/tmp中的文件名,但complete -o filenames选项仅在当前目录为~/tmp时才能正常工作。

有关更多背景信息,请参阅上面的链接。这是我得到的:

$ cat setup
_compTest() {
    local cur baseFolder
    cur="${COMP_WORDS[$COMP_CWORD]}"
    baseFolder=~/tmp
    compopt -o nospace
    COMPREPLY=(  $(
       cd "$baseFolder"
       if [[ ${cur: -1} != "/" && -d $cur ]] ; then
           echo "$cur/"
       else
           compgen -f "$cur"
       fi
      )  )
}
complete -F _compTest aaa

然后我来源:

$ . setup
然后我就可以了

$ aaa <tab><tab>
  • 问题1:在完成列表中的目录名称末尾没有添加斜杠(这样可以轻松地将目录与完成列表中的文件名分开)

  • 问题2:对于aaa a/<tab><tab>,完成列表为a/b a/c a/d,但a/前缀不应该在那里。它应该是b/ c/ d

2 个答案:

答案 0 :(得分:6)

我会把这个函数写成:

_compTest () 
{ 
    local cur; local tmp;  local tmp_escaped; local i;
    _get_comp_words_by_ref cur;
    local _compreply=()
    tmp=~/tmp/
    tmp_escaped=${tmp//\//\\\/}
    cur=$tmp$cur;

    if [ "$1" == "-d" ]; then
        _cd
    else
        _filedir;
    fi;
    for i in "${COMPREPLY[@]}"; do
        [ -d "$i" ] && [ "$i" != "$tmp." ] && [ "$i" != "$tmp.." ] && i="$i/"
        _compreply=("${_compreply[@]}" "$i")
    done

    COMPREPLY=(${_compreply[@]/$tmp_escaped/})
} && complete -o nospace -F _compTest aaa_files

_compTestDir()
{
    _compTest -d
} && complete -o nospace -F _compTestDir aaa_directories

它有3个部分,

  1. $cur添加基本目录前缀 - 〜/ tmp。
  2. 使用用于cd / ls等的标准bash完成例程_filedir
  3. ~/tmp
  4. 中移除COMPREPLY

    仅供记录:您可以使用此逻辑来完成相对于路径的许多其他类型的文件名,例如

    1. 我用它来完成perforce路径//...
    2. 您还可以完成相对于http://localhost/*目录的public_html路径。

答案 1 :(得分:1)

将添加为评论,但我没有声誉...

以@anishsane的answer为基础,如果只有一个完成选项而不是目录,那么您可能要包含nospace选项。例如,程序可能会在您当前完成的参数之后接受另一个参数。如果只有一个选项,我们应该接受该选项,然后转到下一个参数。

为此,请从nospace行中删除complete并添加逻辑以仅在需要时启用nospace

    # [...]

    # Do not include trailing space in results if there is more than one option
    # or if the only option is a directory
    [ "${#_compreply[@]}" -gt 1 ] && compopt -o nospace
    [ "${#_compreply[@]}" -eq 1 ] && [ -d "${_compreply[0]}" ] && compopt -o nospace

    COMPREPLY=(${_compreply[@]/$tmp_escaped/})
} && complete -F _compTest aaa_files