
私はzsh
シェルを使用しており、 内に次のコードがあります.zshrc
:
fpath=(~/.zsh/completion $fpath)
autoload -Uz _vc
autoload -Uz compinit
compinit
~/.zsh/completion
最初の行は、環境変数内に格納されている配列にパスを追加しますfpath
。
_vc
2 行目は、 というカスタム シェル関数の という補完関数をロードしますvc
。 の目的はvc
、特定のフォルダ ( ) 内のファイルを編集することです~/.cheat
。_vc
はファイル で定義されます~/.zsh/completion/_vc
。
最後の 2 行は補完システムを有効にします。
補完関数のコードは次のとおりです_vc
:
#compdef vc
declare -a cheatsheets
cheatsheets="$(ls ~/.cheat)"
_arguments "1:cheatsheets:(${cheatsheets})" && return 0
ここからコピーしました住所、そしてそれを自分のニーズに合わせて調整しました。
~/.cheat
ディレクトリ内に名前に一重引用符を含むファイルが存在しない限り、補完は機能します。しかし、 などのファイルが存在する場合foo'bar
、補完は失敗し、次のエラー メッセージが表示されます。
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
行内の二重引用符cheatsheets="$(ls ~/.cheat)"
を一重引用符に置き換えることで解決策を見つけましたcheatsheets='$(ls ~/.cheat)'
。
Tab
ここで、コマンドの後に を押すと、zsh はを含むvc
内部のファイルを提案します(zsh は一重引用符を自動的にエスケープしているようです)。~/.cheat
foo\'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 はファイルを直接配列に格納できます:
% 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
%
しかし、おそらくローカル配列は必要なく、_files
glob で完了できます。
#compdef vc
_arguments '1:cheatsheets:_files -g "~/.cheat/*"' && return 0
これは完全修飾ファイル パスを返します。検索ディレクトリからファイル名のみが必要な場合は、代わりに以下を使用できます。
#compdef vc
_arguments '1:cheatsheets:_files -W ~/.cheat' && return 0