400*_*Cat 5 shell zsh autocomplete
tar命令的完成系统非常聪明。当我有不同的存档类型时,它会根据使用的 tar 选项提供相关文件:
$ ls
file.tar.xz
file.tar.gz
$ tar xJf f<TAB>
file.tar.xz
$ tar xzf f<TAB>
file.tar.gz
Run Code Online (Sandbox Code Playgroud)
它识别xJf为作用于tar.xz文件,并xzf作用于tar.gz文件。
但是当当前位置存在目录时,它也会提供它以供完成:
$ tar xJf f<TAB>
foo/ file.tar.xz
Run Code Online (Sandbox Code Playgroud)
在提取档案(即tar选项x...)时,我可以告诉完成系统我只想完成文件吗?
但是在创建档案时它仍然应该提供目录(tar选项c...):
tar cpJf foo.tar.xz
Run Code Online (Sandbox Code Playgroud)
我认为这将需要修改tarcommand:的主要完成文件/usr/share/zsh/functions/Completion/Unix/_tar。但是如何?
让完成系统只为每次调用
提供文件tar相对简单。zstyle添加此命令.zshrc
即可解决问题:
zstyle ':completion:*:*:tar:*' 'file-patterns' '*(.)'
Run Code Online (Sandbox Code Playgroud)
这会触发内置函数中的代码_files,通过 glob 限定符将文件模式匹配仅限于纯文件(.)。
不幸的是,它会覆盖所有调用的模式匹配tar,因此示例中基于扩展名的匹配不再起作用,并且在创建存档时目录不是匹配的一部分。更改zsh为更有选择性地忽略目录涉及更深入地了解完成代码。
我们可以通过修改_tar_archive函数来实现这一点(感谢@Gilles 指出如何实现)。该函数已经对提取档案和创建档案进行了单独的处理,因此我们只需要更改用于提取档案的行即可。
这是基于我的系统(macOS 10.15、zsh 5.7.1)中的代码修改后的代码。它直接在提取案例中调用_path_files,因此它只查看文件。原始代码使用_files,它循环遍历文件和目录。
#autoload
# This is used to generate filenames usable as a tar archive. This may
# get one argument, a collection of tar option characters that may be
# used to find out what kind of filename is needed. If no argument is
# given but the parameter `_tar_cmd' is set, that is used.
# If your version of `tar' supports this you may want to complete
# things like `host:file' or `user@host:file' here.
local expl
[[ $# -eq 0 && $+_tar_cmd -ne 0 ]] && set "$_tar_cmd"
_description files expl 'archive file'
if [[ "$1" = *[urtx]* ]]; then
if [[ "$1" = *[zZ]* ]]; then
_path_files "$expl[@]" -g '*.((tar|TAR).(gz|GZ|Z)|tgz)(-.)'
elif [[ "$1" = *[Ijy]* ]]; then
_path_files "$expl[@]" -g '*.(tar|TAR).bz2(-.)'
elif [[ "$1" = *J* ]]; then
_path_files "$expl[@]" -g '*.(tar|TAR).(lzma|xz)(-.)'
elif [[ "$_cmd_variant[$service]" == (gnu|libarchive) ]]; then
_path_files "$expl[@]" -g '*.((tar|TAR)(.gz|.GZ|.Z|.bz2|.lzma|.xz|)|(tbz|tgz|txz))(-.)'
else
_path_files "$expl[@]" -g '*.(tar|TAR)(-.)'
fi
else
_files "$expl[@]"
fi
Run Code Online (Sandbox Code Playgroud)
修改完成系统中的功能可能有点棘手。最好创建副本,而不是更改原始目录中的发布代码。为了快速参考,这些是我的系统所需的命令;其他系统可能有不同的要求。此 Stack Exchange 答案中有更多详细信息: https://unix.stackexchange.com/a/33898/432774
一次改变:
mkdir ~/.zfunc
for d in $fpath; do
[[ -f $d/_tar_archive ]] && cp $d/_tar_archive ~/.zfunc; break
done
# (edit file as noted above)
print 'fpath=( ~/.zfunc "${fpath[@]}" )' >> ~/.zshrc
# (restart shell)
Run Code Online (Sandbox Code Playgroud)
每次代码更改后都需要这些命令。请注意,仅重新启动 shell 可能还不够:
unfunction _tar_archive
autoload -Uz _tar_archive
rm ~/.zcompdump
rm ~/.zcompcache/*
compinit
Run Code Online (Sandbox Code Playgroud)