zsh 完成:使用 tar 提取存档时不提供目录

zsh 完成:使用 tar 提取存档时不提供目录

命令的补全系统tar相当巧妙。当我有不同的存档类型时,它会根据使用的 tar 选项提供相关文件:

$ ls
file.tar.xz
file.tar.gz

$ tar xJf f<TAB>
file.tar.xz

$ tar xzf f<TAB>
file.tar.gz

它识别xJf为对tar.xz文件进行操作,并对文件xzf进行操作。tar.gz

但是当目录存在于当前位置时,它也会提供它来完成:

$ tar xJf f<TAB>
foo/   file.tar.xz

我可以告诉完成系统,在提取档案时我只想完成文件(即tar选项x...)吗?

但在创建档案时它仍然应该提供目录(tar选项c...):

tar cpJf foo.tar.xz 

我认为这需要修改tar命令的主完成文件:/usr/share/zsh/functions/Completion/Unix/_tar。但如何呢?

答案1

使完成系统仅显示以下文件每一个的调用 tar相对简单。zstyle添加此命令.zshrc 即可解决问题:

zstyle ':completion:*:*:tar:*' 'file-patterns' '*(.)'

这会触发内置函数中的代码_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

修改完成系统中的功能可能有点棘手。最好创建副本,而不是更改原始目录中的发布代码。为了快速参考,这些是我的系统所需的命令;其他系统可能有不同的要求。此 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)

每次代码更改后都需要这些命令。请注意,仅重新启动 shell 可能还不够:

unfunction _tar_archive
autoload -Uz _tar_archive

rm ~/.zcompdump
rm ~/.zcompcache/*
compinit

相关内容