zsh補完機能で一重引用符を含むファイル名を処理する方法は?

zsh補完機能で一重引用符を含むファイル名を処理する方法は?

私はシェルを使用しており、zsh私のコードには次のものがあります.zshrc

fpath=(~/.zsh/completion $fpath)
autoload -Uz _vc

autoload -Uz compinit
compinit

最初の行は、~/.zsh/completion環境変数に格納されている配列にパスを追加しますfpath

2_vc行目は、名前付きカスタムシェル関数に名前付き完成関数をロードしますvcvc特定のフォルダ()~/.cheat内のファイルを編集する目的にのみ使用されます。_vcファイルで定義されています~/.zsh/completion/_vc

最後の2行は完成システムを有効にします。

完成した関数のコードは次のとおりです_vc

#compdef vc

declare -a cheatsheets
cheatsheets="$(ls ~/.cheat)"
_arguments "1:cheatsheets:(${cheatsheets})" && return 0

これからコピーしました。住所、私のニーズに合わせて調整しました。

ディレクトリに名前に一重引用符が含まれているファイルがない場合は、~/.cheat完成が機能します。ただし、たとえば、1つがある場合、foo'bar次のエラーメッセージで完了は失敗します。

(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '

行の二重引用符をcheatsheets="$(ls ~/.cheat)"一重引用符に置き換えるソリューションを見つけましたcheatsheets='$(ls ~/.cheat)'

コマンドTabの後にクリックすると、zshはインクルードvc内のファイルを提案します(zshは自動的に一重引用符をエスケープしたようです)。~/.cheatfoo\'bar

しかし、私はそれがどのように、なぜ動作するのか理解していません。一重引用符を使用する場合、変数にはcheatsheetsリテラル文字列を含める必要があります。したがって、$(...)コマンド置換を拡張しないでください。たとえば、次のコマンドを実行すると:

myvar='$(ls)'
echo $myvar    →    outputs `$(ls)` literally

それでは、なぜ'$(ls ~/.cheat)'拡張する必要がありますか_arguments "1:cheatsheets:(${cheatsheets})" && return 0?内部の一重引用符を自動的にエスケープするのはなぜですかfoo'bar

答え1

私たちが仕事に間違った方法でこだわった場合™

#compdef vc

declare -a cheatsheets
cheatsheets=(${(f)"$(ls ~/.cheat/)"})
_arguments '1:cheatsheets:(${cheatsheets})' && return 0

こんな!もちろん、ファイル名に改行文字が含まれている場合は分割されるため中断されます(f)とにかく構文解析lsは悪い考えです。; ZSHはglobファイルを配列に直接置くことができます。

% mkdir ~/.lojban
% touch ~/.lojban/{go\'i,na\ go\'i}
% words=(~/.lojban/*) 
% print -l ${words:t}
go'i
na go'i
% words=(~/.lojban/*(On))
% print -l ${words:t}    
na go'i
go'i
% 

しかし、おそらくローカル配列は必要ありません。_filesglobで行うことができます。

#compdef vc
_arguments '1:cheatsheets:_files -g "~/.cheat/*"' && return 0

検索ディレクトリに使用できる既定のファイル名のみが必要な場合は、完全修飾ファイルパスが返されます。

#compdef vc
_arguments '1:cheatsheets:_files -W ~/.cheat' && return 0

関連情報