在某些情况下,如何调整 zsh 的完成系统来完成“假”文件?
更准确地说,AVFS文件系统通过在每个档案旁边创建一个“假目录”来将档案公开为目录。默认情况下,它会在其安装点下复制整个目录层次结构~/.avfs
。此外,在 下~/.avfs
,对于每个存档文件,例如/tmp/foo.zip
,除了存档文件本身之外~/.avfs/tmp/foo.zip
,还有一个名为 的目录~/.avfs/tmp/foo.zip#
,它公开了存档的内容。然而,这个额外的目录不会出现在列表中~/.avfs/tmp
:它仅在明确请求时才存在。 (这类似于某些自动安装程序的操作方式。)
mountavfs
cd ~/.avfs/tmp/foo.zip\#
当我输入类似上面的命令时,foo.zip#
不会在 下显示为完成~/.avfs/tmp/
,因为没有这样的目录。我如何告诉 zsh 每当有一个文件的完整路径与 1$arc
匹配~/.avfs/**/*.(tgz|zip)
时,它就应该假装有一个名为 的目录${arc}#
?
(请注意,我希望完成功能适用于每次调用_files
or _dirs
,而不仅仅是命令cd
。如果可能的话,我希望不必重复函数的代码_files
。)
和所有其余的扩展
答案1
对于这个假问题,假答案似乎不够; zsh 对自动挂载目录的内置支持在目录 ( fake-files dir:names
) 上运行,而不是在其中的文件模式上运行。这可以方便地将特定的命名文件添加到目录中,这可能适合automount
或sshfs
或 NetApp.snapshot
类型设置,其中要安装的目录是已知的静态名称,ALAS
.
autoload -U compinit
compinit
zstyle ':completion:*' fake-files $HOME/.avfs:'ALAS POOR YORICK'
zshall(1)
说“名称”是字符串,实验表明元字符(例如#
or\#
或'*(e:"echo hi":)'
)以各种方式“不起作用”,所以我不知道如何将 glob 偷偷添加到语句的名称部分fake-files
。 (深入研究Src/Zle/computil.c
可能会准确地揭示名称的含义,但这会需要更多工作。)(此外,在目录位置中使用递归 glob 来命名存档文件并不可行,但再次需要 C 潜水才能看到让fake-files
多少你逃脱了。)
使用compdef
,可以列出.avfs
目录完成情况:
compdef '_files -g $HOME/.avfs/\*' -p \^
function andthehash {
reply=($REPLY $REPLY\#)
}
compdef "_files -g $HOME/.avfs/'*(+andthehash)'" -p \^
但此操作会失败,因为它只会#
在无关紧要的情况下显示文件,因此无法完成它们。 (这种形式有很多富有成效的用途,但不适用于本例。)
使用zstyle
可以让您更接近,如下所示.zshrc
:
autoload -U compinit
compinit
function UnderAVFS {
local pdir
[[ -z $1 ]] && return 1
pdir=$1:a
[[ ! -d $pdir ]] && pdir=$pdir:h
[[ ! -d $pdir || $pdir == . ]] && return 1
while [[ $pdir != / ]]; do
[[ $pdir == $HOME/.avfs ]] && return 0
pdir=$pdir:h
done
return 1
}
function AVFSExtras {
if UnderAVFS $REPLY; then
reply=($REPLY)
# TODO embiggen list of archive suffixes
[[ $REPLY == *.(bz2|tbz2) ]] && reply+=($REPLY\#)
fi
}
# Try to match avfs, otherwise the default pattern. Probably could greatly
# benefit from caching, to avoid hits on UnderAVFS function.
zstyle ':completion:*' file-patterns '*(+AVFSExtras):avfs-files' '%p:all-files'
但是,它只能完成该\#/
位(在具有默认 avfs 设置的 debian squeeze virt 上),但不能完成存档的虚拟文件系统,因此需要额外的工作才能完成存档。我猜测via_call_program ... ls
或类似的东西可以完成位\#
,除非有一种更优雅的方法来zsh
相信不存在的目录确实存在。
坏消息!_path_files
对于假目录,补全不会成功;我怀疑这与 zsh 通配相关。 zsh 4.something on 和 zsh 5.0.8 都出现了该问题。因此,我怀疑完整的修复需要对 进行修补_path_files
,或者编写一些不同的内容,否则可以在这些假目录上完成。如果有人想深入研究失败的原因,可以通过输入ls /root/.avfs/root/sometar.gz\#/
和 然后在该输入结束时输入 来生成完成调试跟踪control-x
,然后?
假设bindkey -e
和绑定到所述组合键。_complete_debug
答案2
在 zsh 中,您可以使用compctl -K
注册自己的函数来生成完成选项。例如:
compctl -f -c -u -r -K myAVFScompletion "*" -tn
然后你需要定义你自己的函数:
myAVFScompletion () {
read -c COMMAND ARGS
# Decide what paths you want to suggest completion for
# by looking inside the ~/.avfs folder.
# Place your suggestions into an array:
reply=( a_path b_path ... )
}
显然,上述内容还需要做更多的工作,但这是一个开始。
可以为 bash 注册类似(但不同)的自定义完成函数。
这是bash 和 zsh 实现的示例。 (它通过从命令的手册页中获取选项来完成命令行选项。)
以下是对完成函数中可能起作用的一些快速猜测:
if [[ -d "$ARGS#" ]]
then reply=( "$ARGS#" )
else reply=
fi
或者也许是这样的:
reply=( $(find ~/.avfs -type f -name "*.tgz" -or -name "*.zip" | sed 's+$+#+') )
祝你好运!