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
command:的主完成文件/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