我想从我的自定义补全中获得以下行为
给定的
$ mkdir foo
$ touch foo faz/bar faz/baz我想要这个
$ 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发挥作用?
发布于 2013-09-28 11:35:47
我遇到了同样的问题。下面是我正在使用的变通方法:
Readline向-o default注册完成功能,例如,complete -o default -F _my_completion.
COMPREPLY=()并让Readline接管(这就是-o default所做的)。这有一个潜在的问题--您可能会使用COMPREPLY=()在不合适的地方拒绝完成,而现在它不再起作用了。在Bash4.0及更高版本中,您可以使用compopt解决此问题,如下所示:
compopt +o default。当COMPREPLY为空时,这将禁用Readline文件名完成。compopt -o default; COMPREPLY=()。换句话说,只有在需要时才启用Readline文件名完成功能。我还没有想出4.0之前的Bash的完整解决方案,但我有一些对我来说足够好的东西。如果有人真的关心这一点,我可以对此进行描述,但希望这些较旧的Bash版本很快就会不再被广泛使用。
发布于 2016-10-25 04:45:05
以前的答案需要bash 4,或者不能与同一命令的其他基于compgen的补全组合。下面的解决方案不需要compopt (因此它适用于bash 3.2),而且它是可组合的(例如,您可以轻松地添加额外的过滤,以仅匹配特定的文件扩展名)。如果不耐烦,跳到底部。
您可以通过直接在命令行上运行compgen来测试compgen:
compgen -f -- $cur其中,$cur是在交互式制表符补全过程中到目前为止要键入的单词。
如果我所在的目录包含文件afile.py、子目录adir和cur=a,则上面的命令将显示以下内容:
$ cur=a
$ compgen -f -- $cur
adir
afile请注意,compgen -f显示了文件和目录。compgen -d仅显示目录:
$ compgen -d -- $cur
adir添加-S /将为每个结果添加一个尾部斜杠:
$ compgen -d -S / -- $cur
adir/现在,我们可以尝试列出所有文件和所有目录:
$ compgen -f -- $cur; compgen -d -S / -- $cur
adir
afile.py
adir/请注意,没有什么可以阻止您多次调用compgen!在您的完成脚本中,您可以这样使用它:
cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(compgen -f -- "$cur"; compgen -d -S / -- "$cur") )不幸的是,这仍然没有给我们提供我们想要的行为,因为如果你输入ad<TAB>,你有两个可能的补全:adir和adir/。因此,ad<TAB>将完成到adir,此时您仍然需要键入/以消除歧义。
我们现在需要的是一个函数,它将返回所有文件,但不返回目录。这就是它:
$ 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:这是一个文件列表--删除与该目录列表匹配的任何文件。
$ 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删除这些标记。
现在我们可以编写一个函数来做我们想做的事情了:
# 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"
}您可以这样使用它:
_mycmd_complete() {
local cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(_mycmd_compgen_filenames "$cur") )
}
complete -o nospace -F _mycmd_complete mycmd请注意,-o nospace意味着在目录的/之后没有空格。对于普通文件,我们在结尾添加了一个带有sed的空格。
将它放在单独的函数中的一个好处是它很容易测试!例如,下面是一个自动化测试:
diff -u <(printf "afile.py \nadir/\n") <(_mycmd_compgen_filenames "a") \
|| { echo "error: unexpected completions"; exit 1; }发布于 2012-10-18 01:02:17
此行为受readline变量mark-directories (和相关mark-symlinked-directories)的影响。我认为这个变量应该在上设置为,以便complete在目录名上打印一个尾部斜杠(bashv3.00.16中的默认值)。看起来compgen的相关行为并没有在目录名后面附加一个斜杠:-\
将mark-directories的值交替设置为on和off,然后重试测试:-
bind 'set mark-directories on'
bind 'set mark-directories off'要使更改在将来的bash调用中永久生效,请将以下内容添加到您的INPUTRC文件中,通常是~/.inputrc:
$if Bash
# append '/' to dirnames
set mark-directories on
$endif在这里可以找到在当前shell中设置readline变量的技巧:https://unix.stackexchange.com/a/27545。我没有决定如何测试readline变量的当前值。
其他想法
也许只是学术兴趣..。
只创建一个目录名列表,并附加一个斜杠:-
compgen -d -S / fhttps://stackoverflow.com/questions/12933362
复制相似问题