在Bash脚本中查找基于文件名的自动完成

时间:2011-01-25 09:11:58

标签: linux bash autocomplete keystroke

我已经想要了很长一段时间的命令行功能,我已经考虑过如何最好地实现它,但我一无所获......

所以我想要的是当我开始输入文件名并点击标签时,例如:

# git add Foo<tab>

我希望它运行find . -name "*$1*"并基本上自动填充匹配的文件到命令行的完整路径。

到目前为止我有什么:

我知道我必须编写一个函数,用我想要的参数调用app 例如git add。之后,它需要捕获tab-keystroke事件并执行上面提到的查找,并显示结果(如果有的话),或填写结果(如果有)。

我无法弄清楚:

如何捕获函数内函数内的tab键事件。

所以基本上是伪代码:

gadd() {git add autocomplete_file_search($1)}

autocomplete_file_search(keyword) {
  if( tab-key-pressed ){
    files = find . -name "*$1*";
    if( filecount > 1 ) {
      show list;
    }
    if( files == 1 ) {
      return files
    }
  }
}

有什么想法吗?

感谢。

4 个答案:

答案 0 :(得分:2)

匹配文件名中的任何位置都相当复杂,我不确定它真的有用。在文件名开头匹配更有意义,更容易实现,甚至递归。

现在,你提到了find作为一个要求,但是bash(从版本4.0开始)也可以递归地查找文件,让bash做那部分应该更有效率。要在bash中递归匹配,可以通过运行shopt -s globstar启用globstar shell选项,然后两个连续的星号**将递归匹配。

接下来,鉴于您希望在git存储库中递归匹配文件,我们最好能够检测到我们实际上是在git存储库中,否则,如果您在/中意外触发它例如,您的提示将在等待bash搜索整个文件系统时挂起。在确定我们是否在git存储库中时,以下函数应该相当有效。给定当前的工作目录,例如/foo/bar/baz,它会查找/foo/bar/baz/.git/foo/bar/.git/foo/.git/.git,如果找到,则返回true,否则返回false。

isgit() {
    local p=$PWD
    while [[ $p ]]; do
        [[ -d $p/.git ]] && return
        p=${p%/*}
    done
    return 1
}

为简单起见,我们将创建一个gadd命令来添加完成。完成功能只能应用于命令的第一个单词。例如。我们可以为git添加完成,但不能添加git add,因此我们会创建一个将git add转换为一个单词的新命令。

gadd() {
    git add "$@"
}

现在为实际完成功能。当通过命中TAB触发时,将使用三个参数调用该函数。 $1是正在完成的命令,$2是正在完成的命令行的当前单词,而$3是该行的前一个单词。所以我们要搜索的文件将由glob **/"$2"*匹配;所有以"$2"开头的文件。我们迭代这些文件名,并将它们附加到COMPREPLY数组。如果COMPREPLY数组在函数完成时只包含一个值,则该字将被该值替换。如果它包含多个值,请再次按Tab键以获取所有匹配项的列表。

shopt -s globstar
_git_add_complete() {
    local file
    isgit || return
    for file in **/"$2"*; do
        # If the glob doesn't match, we'll get the glob itself, so make sure
        # we have an existing file
        [[ -e $file ]] || continue

        # If it's a directory, add a trailing /
        [[ -d $file ]] && file+=/
        COMPREPLY+=( "$file" )
    done
}
complete -F _git_add_complete gadd

将以上三个代码块添加到~/.bashrc,然后打开一个新终端,输入一个git存储库并尝试gadd something<tab>

答案 1 :(得分:1)

你应该看看这个introduction to bash completion。简而言之,bash有一个用于配置和扩展选项卡完成的系统。其他shell也会这样做,每个shell都有不同的设置方式。使用这个系统,没有必要自己做所有事情,并且为命令添加自定义参数完成相对容易。

答案 2 :(得分:0)

我写了git-number,这样我就不必在将文件指定为git时点击标签。

使用git-number,我可以使用数字来表示我希望git处理的文件名。

答案 3 :(得分:0)

这有用吗?

$ cat .bash_completion
_foo()
{
    local files
    cur=${COMP_WORDS[COMP_CWORD]}
    local files=$(for x in `find -type f`; do echo ${x}; done)
    COMPREPLY=( $( compgen -W "${files}" -- ${cur} ) )
    return 0
}
complete -F _foo foo

$ . /etc/bash_completion
$ foo ./[tab]