Shell

zsh 完成:使用 tar 提取存檔時不提供目錄

  • February 11, 2021

命令的完成系統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 

我認為這將需要修改tarcommand:的主完成文件/usr/share/zsh/functions/Completion/Unix/_tar。但是怎麼做?

讓完成系統在每次呼叫時 只顯示文件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

引用自:https://unix.stackexchange.com/questions/633242