在查找文件时让compgen在目录中包含斜杠

Try*_*tøl 20 bash autocomplete bash-completion

我想从我的自定义完成中获得以下行为

特定

$ mkdir foo
$ touch foo faz/bar faz/baz
Run Code Online (Sandbox Code Playgroud)

我想得到这个

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

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

$ foo -u faz/<tab><tab> =>
bar baz
Run Code Online (Sandbox Code Playgroud)

我认为compgen -f f会输出foo faz/,但它的输出foo faz对我没什么帮助.

我是否需要对输出进行后处理,或者是否有一些神奇的选项组合可以使用?

jjl*_*lin 18

我遇到了同样的问题.这是我正在使用的解决方法:

  1. 使用-o default例如注册完成功能complete -o default -F _my_completion.
  2. 当你想要完成一个文件名时,只需设置COMPREPLY=()并让Readline接管(这就是它的-o default作用).

这有一个潜在的问题 - 你可能会COMPREPLY=()在不合适的情况下拒绝完成,现在它将不再起作用.在Bash 4.0及更高版本中,您可以使用compopt以下方法解决此问题:

  1. 在完成功能的顶部,始终运行compopt +o default.当COMPREPLY空时,这将禁用Readline文件名完成.
  2. 如果要完成文件名,请使用compopt -o default; COMPREPLY=().换句话说,仅在需要时启用Readline文件名完成.

我还没有找到4.0之前Bash的完整解决方法,但我有一些适合我的东西.如果有人真正关心我,我可以形容这一点,但希望这些旧的Bash版本很快就会失去常用.

  • 很高兴它有所帮助.顺便说一句,这是一个线程,Bash的作者告诉我,上面我的方法几乎就是这样做的(不幸的是):http://lists.gnu.org/archive/html/bug-bash/2013-10 /msg00002.html (2认同)

Dav*_*ger 6

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


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

compgen -f -- $cur
Run Code Online (Sandbox Code Playgroud)

$cur到目前为止,您在交互式选项卡完成期间输入的单词在哪里。

如果我在一个包含文件afile.py和子目录的目录中adircur=a那么上面的命令将显示以下内容:

$ cur=a
$ compgen -f -- $cur
adir
afile
Run Code Online (Sandbox Code Playgroud)

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

$ compgen -d -- $cur
adir
Run Code Online (Sandbox Code Playgroud)

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

$ compgen -d -S / -- $cur
adir/
Run Code Online (Sandbox Code Playgroud)

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

$ compgen -f -- $cur; compgen -d -S / -- $cur
adir
afile.py
adir/
Run Code Online (Sandbox Code Playgroud)

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

cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(compgen -f -- "$cur"; compgen -d -S / -- "$cur") )
Run Code Online (Sandbox Code Playgroud)

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

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

$ grep -v -F -f <(compgen -d -P '^' -S '$' -- "$cur") \
>     <(compgen -f -P '^' -S '$' -- "$cur") |
>     sed -e 's/^\^//' -e 's/\$$//'
afile.py
Run Code Online (Sandbox Code Playgroud)

让我们分解一下:

  • grep -f file1 file2 表示显示 file2 中与 file1 中的任何模式匹配的行。
  • -F表示 file1 中的模式必须完全匹配(作为子字符串);它们不是正则表达式。
  • -v表示反转匹配:仅显示不在file1 中的行。
  • <(...)是 bash进程替换。它允许您在需要文件的地方运行任何命令。

所以我们告诉 grep:这是一个文件列表——删除任何与此目录列表匹配的文件。

$ grep -v -F -f <(compgen -d -P '^' -S '$' -- "$cur") \
>     <(compgen -f -P '^' -S '$' -- "$cur")
^afile.py$
Run Code Online (Sandbox Code Playgroud)

我已经添加了开始和结束标记与compgen的-P ^-S '$'因为grep的的-F不串匹配,我们不希望删除的文件名一样a-file-with-adir-in-the-middle,只是因为它的中间部分相匹配的目录名。获得文件列表后,我们将使用 sed 删除这些标记。

现在我们可以编写一个函数来执行我们想要的操作:

# 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"
}
Run Code Online (Sandbox Code Playgroud)

你像这样使用它:

_mycmd_complete() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $(_mycmd_compgen_filenames "$cur") )
}
complete -o nospace -F _mycmd_complete mycmd
Run Code Online (Sandbox Code Playgroud)

请注意,这-o nospace意味着您在目录的/. 对于普通文件,我们在 sed 末尾添加了一个空格。

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

diff -u <(printf "afile.py \nadir/\n") <(_mycmd_compgen_filenames "a") \
    || { echo "error: unexpected completions"; exit 1; }
Run Code Online (Sandbox Code Playgroud)


crw*_*crw 5

此行为受readline变量mark-directories(及相关变量mark-symlinked-directories)影响。我相信变量应设置进行complete打印在目录名尾部斜杠(在bash v3.00.16默认值)。似乎与的相关行为compgen不会在目录名称后加上斜杠:-\

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

bind 'set mark-directories on'
bind 'set mark-directories off'
Run Code Online (Sandbox Code Playgroud)

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

$if Bash
# append '/' to dirnames
set mark-directories on
$endif
Run Code Online (Sandbox Code Playgroud)

可以在以下位置找到在当前shell中设置readline变量的提示:https : //unix.stackexchange.com/a/27545。我不确定如何测试readline变量的当前值。

其他想法

也许只有学术兴趣...

仅创建目录名称列表并添加斜杠:-

compgen -d -S / f
Run Code Online (Sandbox Code Playgroud)