首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使compgen在查找文件时包含目录中的斜杠

使compgen在查找文件时包含目录中的斜杠
EN

Stack Overflow用户
提问于 2012-10-17 19:33:44
回答 6查看 4.8K关注 0票数 25

我想从我的自定义补全中获得以下行为

给定的

代码语言:javascript
复制
$ mkdir foo
$ touch foo faz/bar faz/baz

我想要这个

代码语言:javascript
复制
$ foo -u <tab><tab> =>
foo faz/

$ foo -u fa<tab><tab> =>
foo -u faz/

$ foo -u faz/<tab><tab> =>
bar baz

我假设compgen -f f会输出foo faz/,但它输出的是foo faz,这对我没有多大帮助。

我需要对输出进行后处理吗?或者有没有什么神奇的选项组合可以让compgen发挥作用?

EN

回答 6

Stack Overflow用户

发布于 2013-09-28 11:35:47

我遇到了同样的问题。下面是我正在使用的变通方法:

Readline向-o default注册完成功能,例如,complete -o default -F _my_completion.

  • When如果您想完成一个文件名,只需设置COMPREPLY=()并让Readline接管(这就是-o default所做的)。

这有一个潜在的问题--您可能会使用COMPREPLY=()在不合适的地方拒绝完成,而现在它不再起作用了。在Bash4.0及更高版本中,您可以使用compopt解决此问题,如下所示:

  1. 在完成函数的顶部,始终运行compopt +o default。当COMPREPLY为空时,这将禁用Readline文件名完成。
  2. 当您想要完成文件名时,请使用compopt -o default; COMPREPLY=()。换句话说,只有在需要时才启用Readline文件名完成功能。

我还没有想出4.0之前的Bash的完整解决方案,但我有一些对我来说足够好的东西。如果有人真的关心这一点,我可以对此进行描述,但希望这些较旧的Bash版本很快就会不再被广泛使用。

票数 23
EN

Stack Overflow用户

发布于 2016-10-25 04:45:05

以前的答案需要bash 4,或者不能与同一命令的其他基于compgen的补全组合。下面的解决方案不需要compopt (因此它适用于bash 3.2),而且它是可组合的(例如,您可以轻松地添加额外的过滤,以仅匹配特定的文件扩展名)。如果不耐烦,跳到底部。

您可以通过直接在命令行上运行compgen来测试compgen:

代码语言:javascript
复制
compgen -f -- $cur

其中,$cur是在交互式制表符补全过程中到目前为止要键入的单词。

如果我所在的目录包含文件afile.py、子目录adircur=a,则上面的命令将显示以下内容:

代码语言:javascript
复制
$ cur=a
$ compgen -f -- $cur
adir
afile

请注意,compgen -f显示了文件和目录。compgen -d仅显示目录:

代码语言:javascript
复制
$ compgen -d -- $cur
adir

添加-S /将为每个结果添加一个尾部斜杠:

代码语言:javascript
复制
$ compgen -d -S / -- $cur
adir/

现在,我们可以尝试列出所有文件和所有目录:

代码语言:javascript
复制
$ compgen -f -- $cur; compgen -d -S / -- $cur
adir
afile.py
adir/

请注意,没有什么可以阻止您多次调用compgen!在您的完成脚本中,您可以这样使用它:

代码语言:javascript
复制
cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(compgen -f -- "$cur"; compgen -d -S / -- "$cur") )

不幸的是,这仍然没有给我们提供我们想要的行为,因为如果你输入ad<TAB>,你有两个可能的补全:adiradir/。因此,ad<TAB>将完成到adir,此时您仍然需要键入/以消除歧义。

我们现在需要的是一个函数,它将返回所有文件,但不返回目录。这就是它:

代码语言:javascript
复制
$ grep -v -F -f <(compgen -d -P '^' -S '$' -- "$cur") \
>     <(compgen -f -P '^' -S '$' -- "$cur") |
>     sed -e 's/^\^//' -e 's/\$$//'
afile.py

让我们来分析一下:

  • grep -f file1 file2意味着显示与file1.
  • -F中的任何模式匹配的行file1中的模式必须完全匹配(以子字符串的形式);它们不是常规的
    • grep -f file1 file2意味着颠倒匹配:只显示不在file2中的行是bash process substitution。它允许你在需要文件的地方运行任何命令。

因此,我们告诉grep:这是一个文件列表--删除与该目录列表匹配的任何文件。

代码语言:javascript
复制
$ grep -v -F -f <(compgen -d -P '^' -S '$' -- "$cur") \
>     <(compgen -f -P '^' -S '$' -- "$cur")
^afile.py$

我使用compgen的-P ^-S '$'添加了开始和结束标记,因为grep的-F执行子字符串匹配,我们不希望仅仅因为文件名的中间部分与目录名称匹配就删除像a-file-with-adir-in-the-middle这样的文件名。一旦我们有了文件列表,我们就用sed删除这些标记。

现在我们可以编写一个函数来做我们想做的事情了:

代码语言:javascript
复制
# Returns filenames and directories, appending a slash to directory names.
_mycmd_compgen_filenames() {
    local cur="$1"

    # Files, excluding directories:
    grep -v -F -f <(compgen -d -P ^ -S '$' -- "$cur") \
        <(compgen -f -P ^ -S '$' -- "$cur") |
        sed -e 's/^\^//' -e 's/\$$/ /'

    # Directories:
    compgen -d -S / -- "$cur"
}

您可以这样使用它:

代码语言:javascript
复制
_mycmd_complete() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $(_mycmd_compgen_filenames "$cur") )
}
complete -o nospace -F _mycmd_complete mycmd

请注意,-o nospace意味着在目录的/之后没有空格。对于普通文件,我们在结尾添加了一个带有sed的空格。

将它放在单独的函数中的一个好处是它很容易测试!例如,下面是一个自动化测试:

代码语言:javascript
复制
diff -u <(printf "afile.py \nadir/\n") <(_mycmd_compgen_filenames "a") \
    || { echo "error: unexpected completions"; exit 1; }
票数 6
EN

Stack Overflow用户

发布于 2012-10-18 01:02:17

此行为受readline变量mark-directories (和相关mark-symlinked-directories)的影响。我认为这个变量应该在上设置为,以便complete在目录名上打印一个尾部斜杠(bashv3.00.16中的默认值)。看起来compgen的相关行为并没有在目录名后面附加一个斜杠:-\

mark-directories的值交替设置为onoff,然后重试测试:-

代码语言:javascript
复制
bind 'set mark-directories on'
bind 'set mark-directories off'

要使更改在将来的bash调用中永久生效,请将以下内容添加到您的INPUTRC文件中,通常是~/.inputrc

代码语言:javascript
复制
$if Bash
# append '/' to dirnames
set mark-directories on
$endif

在这里可以找到在当前shell中设置readline变量的技巧:https://unix.stackexchange.com/a/27545。我没有决定如何测试readline变量的当前值。

其他想法

也许只是学术兴趣..。

只创建一个目录名列表,并附加一个斜杠:-

代码语言:javascript
复制
compgen -d -S / f
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12933362

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档